从顶点和索引缓冲区呈现 (Direct3D 9)

Direct3D 支持索引和非索引绘图方法。 索引方法对所有顶点组件使用单个索引集。 顶点数据存储在顶点缓冲区中,索引数据存储在索引缓冲区中。 下面列出了使用顶点和索引缓冲区绘制基元的一些常见方案。

这些示例比较 IDirect3DDevice9::D rawPrimitiveIDirect3DDevice9::D rawIndexedPrimitive

方案 1:在不编制索引的情况下绘制两个三角形

假设你想要绘制下图中显示的象限。

illustration of a square that consists of two triangles

如果使用三角形列表基元类型呈现这两个三角形,则每个三角形将存储为 3 个单独的顶点,从而导致与下图类似的顶点缓冲区。

diagram of a vertex buffer that defines three vertices for two triangles

绘图调用非常简单;从顶点缓冲区中的位置 0 开始,绘制两个三角形。 如果启用剔除,顶点的顺序将很重要。 此示例假定默认的反时针剔除状态,因此必须按顺时针顺序绘制可见三角形。 三角形列表基元类型只是从每个三角形的缓冲区中按线性顺序读取三个顶点,因此此调用将绘制三角形 (0、1、2) 和 (3、4、5) :

DrawPrimitive( D3DPT_TRIANGLELIST, // PrimitiveType
               0,                  // StartVertex
               2 );                // PrimitiveCount

方案 2:使用索引绘制两个三角形

如你注意到,顶点缓冲区包含位置 0 和 4、2 和 5 中的重复数据。 这很有意义,因为两个三角形共享两个常见顶点。 此重复数据是浪费的,可以使用索引缓冲区压缩顶点缓冲区。 较小的顶点缓冲区减少了必须发送到图形适配器的顶点数据量。 更重要的是,使用索引缓冲区允许适配器将顶点存储在顶点缓存中;如果绘制的基元包含最近使用的顶点,则可以从缓存中提取该顶点,而不是从顶点缓冲区中读取它,这会导致性能大幅提高。

索引缓冲区“索引”到顶点缓冲区中,因此每个唯一顶点只需要存储在顶点缓冲区中一次。 下图显示了早期绘图方案的索引方法。

diagram of an index buffer for the earlier vertex buffer

索引缓冲区存储VB索引值,这些值引用顶点缓冲区中的特定顶点。 顶点缓冲区可以视为顶点数组,因此VB索引只是目标顶点的顶点缓冲区中的索引。 同样,IB 索引是索引缓冲区中的索引。 如果你不小心,这很快就会变得非常混乱,因此请确保你清楚使用的词汇:VB索引值索引到顶点缓冲区,IB 索引值索引到索引缓冲区中,索引缓冲区本身存储VB索引值。

绘图调用如下所示。 下一个绘图方案以长度讨论所有参数的含义;现在,只需注意,此调用再次指示 Direct3D 呈现包含两个三角形的三角形列表,从索引缓冲区中的位置 0 开始。 此调用将按与之前完全相同的顺序绘制相同的两个三角形,确保正确的顺时针方向:

   
DrawIndexedPrimitive( D3DPT_TRIANGLELIST, // PrimitiveType
                    0,                  // BaseVertexIndex
                    0,                  // MinIndex
                    4,                  // NumVertices
                    0,                  // StartIndex
                    2 );                // PrimitiveCount

方案 3:使用索引绘制一个三角形

假设现在只想绘制第二个三角形,但你想要使用绘制整个象限时使用的同一顶点缓冲区和索引缓冲区,如下图所示。

diagram of the index buffer and vertex buffer for the second triangle

对于此绘图调用,使用的第一个 IB 索引为 3;此值称为 StartIndex。 使用的最低VB索引为 0;此值称为 MinIndex。 即使只有三个顶点需要绘制三角形,但这些三个顶点分布在顶点缓冲区的四个相邻位置:绘图调用所需的连续顶点缓冲区内存中的位置数称为 NumVertices,并将在此调用中设置为 4。 MinIndex 和 NumVertices 值确实只是帮助 Direct3D 优化软件顶点处理期间的内存访问的提示,并且只需设置为以性能的价格包括整个顶点缓冲区。

下面是单个三角形事例的绘图调用;接下来将解释 BaseVertexIndex 参数的含义:

   
DrawIndexedPrimitive( D3DPT_TRIANGLELIST, // PrimitiveType
                    0,                  // BaseVertexIndex
                    0,                  // MinIndex
                    4,                  // NumVertices
                    3,                  // StartIndex
                    1 );                // PrimitiveCount

方案 4:使用偏移索引绘制一个三角形

BaseVertexIndex 是有效添加到索引缓冲区中存储的每个VB索引的值。 例如,如果在上一次调用期间为 BaseVertexIndex 传入了值 50,则其功能上与在 DrawIndexedPrimitive 调用期间使用索引缓冲区相同:

diagram of an index buffer with a value of 50 for basevertexindex

此值很少设置为 0 以外的任何值,但如果要将索引缓冲区与顶点缓冲区分离,则非常有用:如果在为特定网格填充索引缓冲区时,顶点缓冲区中网格的位置尚不已知,则只需假装网格顶点将位于顶点缓冲区的开头:在进行绘图调用时,只需将实际起始位置作为 BaseVertexIndex 传递。

使用单个索引缓冲区绘制网格的多个实例时,也可以使用此方法:例如,如果顶点缓冲区包含两个具有相同绘制顺序的网格,但略有不同顶点 (可能不同的漫射颜色或纹理坐标) ,则可以使用 BaseVertexIndex 的不同值绘制这两个网格。 进一步执行此概念后,可以使用一个索引缓冲区来绘制网格的多个实例,每个实例包含在不同的顶点缓冲区中,只需循环访问哪个顶点缓冲区处于活动状态并根据需要调整 BaseVertexIndex。 请注意,BaseVertexIndex 值也会自动添加到 MinIndex 参数中,当你看到它的使用方式时,这很有意义:

假装现在,我们再次希望仅使用与以前相同的索引缓冲区绘制象限的第二个三角形;但是,使用另一个顶点缓冲区,其中象限位于索引 50 VB。 四个顶点的相对顺序保持不变,顶点缓冲区中的起始位置不同。 索引缓冲区和顶点缓冲区如下图所示。

diagram of the index buffer and vertex buffer with a vb index of 50

下面是适当的绘图调用;请注意,BaseVertexIndex 是唯一从上一个方案更改的值:

   
DrawIndexedPrimitive( D3DPT_TRIANGLELIST, // PrimitiveType
                    50,                 // BaseVertexIndex
                    0,                  // MinIndex
                    4,                  // NumVertices
                    3,                  // StartIndex
                    1 );                // PrimitiveCount

呈现基元