c++ - 如何读取 Blender 的默认立方体?
问题描述
我正在学习 DirectX 11,在查看 Blender 的默认立方体后我感到非常困惑,我已经将其导出为 Wavefront OBJ 格式,这是我得到的文件:
# Blender v2.92.0 OBJ File: ''
# www.blender.org
mtllib untitled.mtl
o Cube
v 1.000000 1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 1.000000 -1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 1.000000
vt 0.625000 0.500000
vt 0.875000 0.500000
vt 0.875000 0.750000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.125000 0.500000
vt 0.375000 0.500000
vt 0.125000 0.750000
vn 0.0000 1.0000 0.0000
vn 0.0000 0.0000 1.0000
vn -1.0000 0.0000 0.0000
vn 0.0000 -1.0000 0.0000
vn 1.0000 0.0000 0.0000
vn 0.0000 0.0000 -1.0000
usemtl Material
s off
f 1/1/1 5/2/1 7/3/1 3/4/1
f 4/5/2 3/4/2 7/6/2 8/7/2
f 8/8/3 7/9/3 5/10/3 6/11/3
f 6/12/4 2/13/4 4/5/4 8/14/4
f 2/13/5 1/1/5 3/4/5 4/5/5
f 6/11/6 5/10/6 1/1/6 2/13/6
我知道它渲染的秘诀是使用提供的索引,但我不确定我应该如何为这些数据实现缓冲区,如果顶点、uv 和法线的数量相同,我可以将它们组织成一个数组结构,但如果它们的数量不同,我不知道我怎么能这样做并创建一个缓冲区。
解决方案
Wavefront OBJ 格式非常老派,但它已经存在很长时间了,所以它很容易理解并且很容易解析。也就是说,像 Blender 这样的 3D 渲染器包不必以您通常用于运行时渲染的相同格式存储它们的数据,因此希望做一些工作来将每种数据的单个索引的 Wavefront OBJ 方法更改为单流渲染的顶点格式。
另一个挑战是 Wavefront OBJ 将面存储为“n-gons”,您需要将其转换为三角形以使用 Direct3D 11 进行渲染。您还可以遇到不同数组的相对索引(即负数)。然后是处理mtl
文件。
可以在 GitHub 上找到使用 C++ 进行所有文本文件解析和转换的示例:WavefrontReader.h,此处记录。
WaveFrontReader<uint16_t> wfReader;
Microsoft::WRL::ComPtr<ID3D11Buffer> vertexBuffer;
Microsoft::WRL::ComPtr<ID3D11Buffer> indexBuffer;
HRESULT hr = wfReader.Load(L"cube.obj");
if (FAILED(hr))
// Error
D3D11_BUFFER_DESC bufferDesc = {};
bufferDesc.ByteWidth = static_cast<UINT>(wfReader.vertices.size() * sizeof(WaveFrontReader::Vertex));
bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bufferDesc.Usage = D3D11_USAGE_DEFAULT;
D3D11_SUBRESOURCE_DATA initData = { wfReader.vertices.data(), 0, 0 };
hr = device->CreateBuffer(&bufferDesc, &initData, &vertexBuffer);
if (FAILED(hr))
// Error
bufferDesc.ByteWidth = static_cast<UINT>(wfReader.indices.size() * sizeof(uint16_t));
bufferDesc.bindFlags = D3D11_BIND_INDEX_BUFFER;
initData.pSysMem = wfReader.indices.data();
hr = device->CreateBuffer(&bufferDesc, &initData, &indexBuffer);
if (FAILED(hr))
// Error
UINT vertexStride = sizeof(WaveFrontReader::Vertex);
uint vertexOffset = 0;
deviceContext->IASetVertexBuffers(0, 1, vertexBuffer.GetAddressOf(), &vertexStride, &vertexOffset);
deviceContext->IASetIndexBuffer(indexBuffer.Get(), DXGI_FORMAT_R16_UINT, 0);
一个常见的策略——至少是游戏——是离线进行所有解析并写出一个很好的、GPU 友好的运行时文件格式,您可以将其读入内存并进行渲染。对于 DirectX,有许多“示例”格式,例如VBO
、SDKMESH
或 Visual Studio 的CMO
. 使用这些工具通常会使在运行时读取顶点和索引缓冲区变得微不足道。请参阅DirectXMesh的meshconvert、DirectX SDK 示例内容导出器和Visual Studio 内容管道。加载和渲染所有这些格式的代码可以在DirectX 工具包中找到。
当然,对于像立方体这样简单的常见形状,您可以直接在内存中创建它,而不必首先经历解析模型的所有麻烦。这就是DirectX 工具包
GeometricPrimitive
的作用。
注意:即使您使用多流渲染,您仍然必须复制数据,因为所有流都必须为每个顶点使用来自 IB 的相同索引。由于您已经在 CPU 上进行了一些数据重新排列,因此您不妨创建一个单流打包的 Vertex,它比 2 或 3 个流更有效。
推荐阅读
- python - 在 Django models.py 中将 .csv 字符串数据转换为 DateField 类型
- api - 在颤振中构建应用程序时无法从 API 获取数据
- html - 通过 main.scss 更改高度
- javascript - 将 Google Tag Manager (gtm) 安装到 Angular 应用程序
- swift - 在日历 Swift 中更改可见月份
- python-3.x - Asyncio 任务同步运行
- redis - 限制/清理 Redis 密钥
- bash - cmake中的bash命令将结果与阈值进行比较
- powerbi - PowerBI 日期过滤未将过滤器应用于报告内的潜在客户表
- angular - 服务功能无法在 Angular 中访问“this”