gpt4 book ai didi

c++ - 顶点和索引缓冲区如何在 DirectX11 中与顶点、法线和 Texcoords 一起工作

转载 作者:行者123 更新时间:2023-11-27 23:45:13 27 4
gpt4 key购买 nike

我不明白 DirectX11 如何理解顶点缓冲区中的哪些值是顶点,哪些是法线,哪些是纹理坐标

例如:

以下代码有效,但它将模型绘制为全白。但是法线和顶点绘制正确

std::vector<float> vertex_buffer;
for (int i = 0, j = 0; i < num_vertices; i+=3, j+=2)
{
vertex_buffer.push_back(attrib.vertices[i + 0]);
vertex_buffer.push_back(attrib.vertices[i + 1]);
vertex_buffer.push_back(attrib.vertices[i + 2]);
vertex_buffer.push_back(attrib.normals[i + 0]);
vertex_buffer.push_back(attrib.normals[i + 1]);
vertex_buffer.push_back(attrib.normals[i + 2]);
vertex_buffer.push_back(0.0F);
vertex_buffer.push_back(0.0F);
vertex_buffer.push_back(0.0F);
}

std::vector<UINT> index_buffer;
for (int i = 0, j = 0; i < num_indices; i+=3, j+=2)
{
index_buffer.push_back(shapes[0].mesh.indices[i + 0].vertex_index);
index_buffer.push_back(shapes[0].mesh.indices[i + 1].vertex_index);
index_buffer.push_back(shapes[0].mesh.indices[i + 2].vertex_index);
index_buffer.push_back(shapes[0].mesh.indices[i + 0].normal_index);
index_buffer.push_back(shapes[0].mesh.indices[i + 1].normal_index);
index_buffer.push_back(shapes[0].mesh.indices[i + 2].normal_index);
index_buffer.push_back(0);
index_buffer.push_back(0);
index_buffer.push_back(0);
}

例如,上面的代码会产生这样的结果: enter image description here

但是,如果我开始更改索引缓冲区中 9 个值的最后 3 个值中的任何内容,模型将错误地绘制顶点。

这里我修改为使用texcoords(我使用 TinyObjLoader 导入 o​​bj 文件,我不知道为什么每个顶点有 3 个纹理坐标,而不是 2 个)

std::vector<float> vertex_buffer;
for (int i = 0, j = 0; i < num_vertices; i += 3, j += 2)
{
vertex_buffer.push_back(attrib.vertices[i + 0]);
vertex_buffer.push_back(attrib.vertices[i + 1]);
vertex_buffer.push_back(attrib.vertices[i + 2]);
vertex_buffer.push_back(attrib.normals[i + 0]);
vertex_buffer.push_back(attrib.normals[i + 1]);
vertex_buffer.push_back(attrib.normals[i + 2]);
vertex_buffer.push_back(attrib.texcoords[i + 0]);
vertex_buffer.push_back(attrib.texcoords[i + 1]);
vertex_buffer.push_back(attrib.texcoords[i + 2]);
}

std::vector<UINT> index_buffer;
for (int i = 0, j = 0; i < num_indices; i += 3, j += 2)
{
index_buffer.push_back(shapes[0].mesh.indices[i + 0].vertex_index);
index_buffer.push_back(shapes[0].mesh.indices[i + 1].vertex_index);
index_buffer.push_back(shapes[0].mesh.indices[i + 2].vertex_index);
index_buffer.push_back(shapes[0].mesh.indices[i + 0].normal_index);
index_buffer.push_back(shapes[0].mesh.indices[i + 1].normal_index);
index_buffer.push_back(shapes[0].mesh.indices[i + 2].normal_index);
index_buffer.push_back(shapes[0].mesh.indices[i + 0].texcoord_index);
index_buffer.push_back(shapes[0].mesh.indices[i + 1].texcoord_index);
index_buffer.push_back(shapes[0].mesh.indices[i + 2].texcoord_index);
}

我得到这个结果:

enter image description here

显然,这里不仅仅是受影响的纹理,顶点顺序也被打乱了。但我只更改缓冲区中应该用于 texcoords 的字段。为什么它会影响顶点。为什么顶点和法线坐标/值有效,但纹理坐标却无效。

DirectX 如何知道 IndexBuffer 中的哪些索引指的是顶点,哪些指的是法线,哪些指的是纹理坐标

另外,对于这个模型,我必须使用 36 的 Vertex Stride,当我将条目从 9 移动到 8 并将 vetex stride 更改为 32 时,情况更糟。

DirectX 是否会自动将步长内的前 3 个值分配给顶点,将 next3 分配给法线,然后将接下来的 2 个值分配给 texcoordinates?这是它的工作原理吗?

谢谢,

最佳答案

Direct3D 输入汇编器并不像您假设的那样灵活。它从索引缓冲区中获取一个单个索引,并使用该值从 1 个或多个绑定(bind)顶点缓冲区中查找相同顶点。然后将整个顶点发送到顶点着色器的单次调用。

输入布局会告诉您需要知道的一切。例如,这是一个非常简单的输入布局:

{ "SV_Position", 0, DXGI_FORMAT_R32G32B32_FLOAT,    0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },

这对应于像这样的顶点结构:

struct Vertex
{
XMFLOAT3 position;
XMFLOAT3 normal;
XMFLOAT2 textureCoordinate;
};

在这种情况下,您会将一个单个 顶点缓冲区绑定(bind)到系统,当然还有一个单个 索引缓冲区。 VB 的步幅为 sizeof(Vertex) 或 32 字节,这被认为是大多数硬件的最佳大小。

它会使用像这样的伪代码:

// StartIndexLocation, BaseVertexLocation, IndexCount are DrawIndexed parameters
// stride and offset are IASetVertexBuffers parameters
for(I = 0; I < IndexCount; I++)
{
uint16_t/uint32_t index = IndexBuffer[I + StartIndexLocation];

Vertex v = VertexBuffer_Bytes[((index + BaseVertexLocation) * stride) + offset];

VertexShader(v);
}

您还可以创建采用多个 VB 的多流输入布局。这是 3 流输入布局的示例:

{ "SV_Position", 0, DXGI_FORMAT_R32G32B32_FLOAT,    0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 1, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 2, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },

这里对应三个顶点结构:

struct Vertex1
{
XMFLOAT3 position;
};

struct Vertex2
{
XMFLOAT3 normal;
};

struct Vertex3
{
XMFLOAT2 textureCoordinate;
};

对于三个 绑定(bind)的顶点缓冲区,您可以使用 12、12 和 8 的步长。仍然只有一个单个索引缓冲区,因此特定顶点的所有数据必须在所有三个 VB 的相同索引中。

它会使用像这样的伪代码:

for(I = 0; I < IndexCount; I++)
{
uint16_t/uint32_t index = IndexBuffer[I + StartIndexLocation];

Vertex1 v1 = VertexBuffer0_Bytes[((index + BaseVertexLocation) * stride0) + offset0];
Vertex2 v2 = VertexBuffer1_Bytes[((index + BaseVertexLocation) * stride1) + offset1];
Vertex3 v3 = VertexBuffer2_Bytes[((index + BaseVertexLocation) * stride2) + offset2];

VertexShader(v1, v2, v3);
}

虽然 WaveFront OBJ 等几何文件格式和 CAD/3D 艺术程序的内部数据结构通常为每个顶点使用多个索引以获得更紧凑的内存结构,但您不能直接渲染这样的使用 Direct3D 或 OpenGL 的数据。您必须通过复制数据将其转换为交错形式。

std::vector<XMFLOAT3> positions;
std::vector<XMFLOAT3> normals;
std::vector<XMFLOAT2> texcoords;
// Load these three from the file

std::vector<Vertex> vertexBuffer;
std::vector<uint32_t> indexBuffer;

for each face in WaveFront OBJ:
for each vertex in the face:
Vertex v;
v.position = positions[vertexIndex];
v.normal = normal[normalIndex];
v.textureCoordinate = texcoords[textureIndex];

uint32_t index = AddVertex(vertexIndex, &vertex, vertexCache);
indexBuffer.push_back(index);

// Helper function to try to minimize vertex duplication
typedef std::unordered_multimap<UINT, UINT> VertexCache;

uint32_t AddVertex(UINT hash, const Vertex* pVertex, VertexCache& cache)
{
auto f = cache.equal_range(hash);

for (auto it = f.first; it != f.second; ++it)
{
auto& tv = vertexBuffer[it->second];

if (0 == memcmp(pVertex, &tv, sizeof(Vertex)))
{
return it->second;
}
}

uint32_t index = static_cast<uint32_t>(vertices.size());
vertexBuffer.emplace_back(*pVertex);

VertexCache::value_type entry(hash, index);
cache.insert(entry);
return index;
}

参见 WaveFrontReader.h .虽然我的阅读器实现并不完美,但它确实处理了您的代码忽略的许多问题,例如负索引值、将 n 边形转换为三角形等。

关于c++ - 顶点和索引缓冲区如何在 DirectX11 中与顶点、法线和 Texcoords 一起工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51274628/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com