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

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

这些示例比较了 IDirect3DDevice9::D rawPrimitiveIDirect3DDevice9::D rawIndexedPrimitive 的使用

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

假设要绘制下图中所示的象限。

由两个三角形组成的正方形的插图

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

为两个三角形定义三个顶点的顶点缓冲区示意图

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

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

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

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

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

早期顶点缓冲区的索引缓冲区示意图

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

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

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

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

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

第二个三角形的索引缓冲区和顶点缓冲区示意图

对于此绘图调用,使用的第一个 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 调用期间使用索引缓冲区相同:

basevertexindex 值为 50 的索引缓冲区示意图

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

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

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

vb 索引为 50 的索引缓冲区和顶点缓冲区的关系图

下面是适当的绘图调用:请注意,BaseVertexIndex 是唯一与上一方案相比已更改的值:

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

呈现基元