Direct3D输入汇编程序并不像您假设的那样灵活。它需要一个
单一的
索引缓冲区中的索引,并使用该值查找
相同的
来自1个或多个绑定顶点缓冲区的顶点。然后将整个顶点发送到一个顶点着色器的单个调用。
输入布局告诉您需要知道的一切。例如,这里是一个非常简单的输入布局:
{ "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;
};
在这种情况下,您将绑定
单一的
顶点缓冲到系统,当然还有
单一的
索引缓冲区。VB的步伐将是
sizeof(Vertex)
或32字节,这被认为是大多数硬件的最佳大小。
它将使用类似于以下伪代码的代码:
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;
};
你可以用12、12和8的步幅
三
绑定顶点缓冲区。仍然只有一个
单一的
索引缓冲区,因此特定顶点的所有数据必须在所有三个VBS的同一索引中。
它将使用类似于以下伪代码的代码:
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;
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);
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-gons转换为三角形等。