在DX10/DX11中使用多个顶点缓冲区

6
我正在编写一个C++ DirectX 11渲染器。
我编写了一个COLLADA 1.4.1加载器,以导入COLLADA数据来支持骨骼动画。
目前我正在验证加载器(之前我用不同的技术编写过另一个渲染器,并已经支持过COLLADA),但在将COLLADA与DX10/11配对时遇到了问题。
我有三个不同的顶点数据缓冲区:
一个是唯一顶点位置的缓冲区。 一个是唯一法线向量的缓冲区。 一个是唯一纹理坐标的缓冲区。
这些缓冲区中包含不同长度的数组(位置有2910个元素,法线超过9000个,纹理坐标大约为3200个)。
COLLADA提供一个三角形列表,其中给出了每个三角形在这些数组中的索引(最初可能比较冗长和奇怪,但熟悉后就变得简单了)。
知道DX10/11支持多个顶点缓冲区后,我认为应该使用这些缓冲区的索引填充DX10/11索引缓冲区* 和 *(这是重点)。对于给定三角形的某个点,这些索引可能是不同的。
换句话说,我可以设置这三个顶点缓冲区,设置正确的输入布局,然后在索引缓冲区中放置相当于:
l_aIndexBuffer[ NumberOfTriangles * 3 ]

for( i = 0; i < NumberOfTriangles; i++ )
{
    l_aIndexBufferData.add( triangle[i].Point1.PositionIndex )
    l_aIndexBufferData.add( triangle[i].Point1.NormalIndex )
    l_aIndexBufferData.add( triangle[i].Point1.TextureCoordinateIndex )
}

关于在DirectX中使用多个顶点缓冲区的文档似乎没有提供关于它如何影响索引缓冲区的任何信息(稍后会详细说明)。

以这种方式运行代码会产生奇怪的渲染结果,我可以看到我的网格被间歇性地正确绘制(奇怪的多边形,但大约三分之一的点在正确的位置——暗示-暗示)。

我想此时可能是我的数据或索引弄错了(昨天),所以我费力地验证了所有内容,因此我认为我搞错了输入或其他方面的问题。我通过使用法线和纹理缓冲区的值交替设置像素着色器使用的颜色值来消除了这个问题,颜色是正确的,因此我没有受到填充问题的困扰。

最终,我得出结论,DX10/11必须希望以不同的方式对数据进行排序,因此我尝试以这种方式存储索引:

indices.add( Point1Position index )
indices.add( Point2Position index )
indices.add( Point3Position index )
indices.add( Point1Normal index )
indices.add( Point2Normal index )
indices.add( Point3Normal index )
indices.add( Point1TexCoord index )
indices.add( Point2TexCoord index )
indices.add( Point3TexCoord index )

奇怪的是,这产生了一个看起来正确三分之一的渲染网格-提示-提示。
然后我推测,可能DX10/DX11希望索引存储在“按顶点缓冲区”的位置,这意味着我将先添加所有三角形的位置索引,然后添加所有三角形的法线索引,最后添加所有三角形的纹理坐标索引。
这样又产生了另外一个看起来正确三分之一的网格。
这使我想到-好吧,肯定DX10/11不会为您提供从多个顶点缓冲区流式传输的能力,然后实际上只期望每个三角形点有一个索引?
仅将索引包括到位置的顶点缓冲区中会产生一个正确呈现的网格,但不幸的是它使用了错误的法线和纹理坐标。
看起来将法线和纹理坐标索引放入索引缓冲区会导致在正确呈现的网格上发生错误的绘图。
这是预期行为吗?
多个顶点缓冲区-一个索引缓冲区,索引缓冲区只能对于一个三角形点有一个单一的索引?
对我来说这真的没有意义。
求助!

哦,我的天啊,我从来不知道你可以以那种方式分割顶点数据! - Lucius
我不久前也遇到了同样的问题,在寻找解决方案时,我发现了这个答案。它真的帮助我更深入地了解了这个问题。它可能也会对你有所帮助。 - Krienie
1个回答

3

我脑海中浮现的第一件事:

所有支持计算着色器的硬件(几乎等同于所有DirectX 10及以上版本)也支持ByteAddressBuffer和大部分支持StructuredBuffer。因此,您可以将数组绑定为SRV并在着色器中随机访问其任何元素。

类似于以下代码(未经测试,仅为伪代码):

// Indices passed as vertex buffer to shader
// Think of them as of "references" to real data
struct VS_INPUT
{
    uint posidx;
    uint noridx;
    uint texidx;
}

// The real vertex data 
// You pass it as structured buffers (similar to textures)
StructuredBuffer<float3> pos : register (t0);
StructuredBuffer<float3> nor : register (t1);
StructuredBuffer<float2> tex : register (t2);


VS_OUTPUT main(VS_INPUT indices)
{
    // in shader you read data for current vertex
    float3 pos = pos[indices.posidx];
    float3 nor = nor[indices.noridx];
    float2 tex = tex[indices.texidx];

    // here you do something
}

让我们称之为“计算着色器方法”。您必须使用DirectX 11 API。
此外,您可以以相同的方式绑定索引并在着色器中执行某些操作。在这种情况下,您需要找出当前索引ID。可能可以从SV_VertexId中获取它。
也许您可以绕过这些缓冲区,并以其他方式绑定数据(与DirectX 9兼容的纹理采样!O_o)。
希望这有所帮助!

这是一个有趣的解决方法,我得试一下。我很想看看在每个网格上设置这个的性能方面。谢谢。 - WTH

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接