【问题标题】:How do I read Blender's default cube?如何读取 Blender 的默认立方体?
【发布时间】:2021-09-20 02:07:39
【问题描述】:

我正在学习 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 和法线的数量相同,我可以将它们组织在一个结构数组,但如果它们的编号不同,我不知道我该怎么做并创建一个缓冲区。

【问题讨论】:

    标签: c++ directx-11


    【解决方案1】:

    Wavefront OBJ format 非常老派,但它已经存在很长时间了,所以它很好理解并且相当容易解析。也就是说,像 Blender 这样的 3D 渲染器包不必以您通常用于运行时渲染的相同格式存储它们的数据,因此希望做一些工作来将每种数据的单个索引的 Wavefront OBJ 方法更改为单流渲染的顶点格式。

    另一个挑战是 Wavefront OBJ 将面存储为“n-gons”,您需要将其转换为三角形才能使用 Direct3D 11 进行渲染。您还可以遇到不同数组的相对索引(即负数)。然后是处理mtl 文件。

    可以在 GitHub 上找到一个使用 C++ 进行所有文本文件解析和转换的示例:WavefrontReader.h,其中记录了 here

    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,有许多“示例”格式,例如 VBOSDKMESH 或 Visual Studio 的 CMO。使用这些工具通常会使在运行时读取顶点和索引缓冲区变得微不足道。请参阅 DirectXMeshmeshconvertDirectX SDK Samples Content ExporterVisual Studio Content pipeline。加载和渲染所有这些格式的代码可以在DirectX Tool Kit 中找到。

    当然,对于像立方体这样简单的常见形状,您可以直接在内存中创建它,而不必首先经历解析模型的所有麻烦。这就是 DirectX 工具包 中的GeometricPrimitive 所做的。

    注意:即使您使用multi-stream rendering,您仍然必须复制数据,因为所有流都必须为每个顶点使用来自 IB 的相同索引。由于您已经在 CPU 上进行了一些数据重新排列,因此您不妨创建一个单流打包的 Vertex,它比 2 或 3 个流更有效。

    【讨论】:

      猜你喜欢
      • 2022-08-24
      • 2011-11-28
      • 1970-01-01
      • 2021-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多