我什么时候应该使用OpenGL顶点的索引数组?

Jon*_*ley 41 arrays opengl indexed vertex

我试图清楚地知道何时应该使用带有gl [Multi] DrawElements等绘制的OpenGL顶点的索引数组,而不是我应该简单地使用连续的顶点数组,用gl [Multi] DrawArrays绘制.

(更新:我得到的回复中的共识是,应该始终使用索引顶点.)

我已经多次在这个问题上来回走动了,所以我将概述我目前的理解,希望有人可以告诉我,我现在终于或多或少正确,或者指出我遗留的误解在哪里.具体来说,我有三个结论,粗体.如果错误请纠正.

一个简单的例子是如果我的几何体由网格组成以形成曲面.在这种情况下,网格中间的顶点对于使用顶点的每个三角形将具有相同的属性(位置,法线,颜色,纹理坐标等).

这使我得出结论:

对于接缝很少的几何体,索引数组是一个巨大的胜利.

始终遵循规则1,除了:

对于非常"块状"的几何体,其中每个边都代表一个接缝,索引数组的好处不那么明显.以一个简单的立方体为例,虽然每个顶点用于三个不同的面,但我们不能在它们之间共享顶点,因为对于单个顶点,表面法线(以及可能的其他东西,如颜色和纹理合作) )每张脸都会有所不同.因此,我们需要在我们的数组中明确引入冗余顶点位置,以便可以使用不同的法线等多次使用相同的位置.这意味着索引数组的使用较少.

例如,渲染立方体的单个面时:

 0     1
  o---o
  |\  |
  | \ |
  |  \|
  o---o
 3     2
Run Code Online (Sandbox Code Playgroud)

(这可以单独考虑,因为这个面和所有相邻面之间的接缝意味着这些顶点之间不能共享这些顶点)

如果使用GL_TRIANGLE_FAN(或_STRIP)进行渲染,则可以渲染立方体的每个面:

verts  = [v0, v1, v2, v3]
colors = [c0, c0, c0, c0]
normal = [n0, n0, n0, n0]
Run Code Online (Sandbox Code Playgroud)

添加索引不允许我们简化此操作.

由此我得出结论:

2.当渲染所有接缝或大多数接缝的几何体时,当使用GL_TRIANGLE_STRIP或_FAN时,我应该永远不要使用索引数组,而应该总是使用gl [Multi] DrawArrays.

(更新:回复表明这个结论是错误的.即使索引不允许我们在这里减小数组的大小,仍然应该使用它们,因为其他性能优势,如评论中所讨论的)

规则2的唯一例外是:

当使用GL_TRIANGLES(而不是条带或扇形)时,一半的顶点仍然可以重复使用两次,具有相同的法线和颜色等,因为每个立方体面都呈现为两个独立的三角形.同样,对于相同的单个立方体面:

 0     1
  o---o
  |\  |
  | \ |
  |  \|
  o---o
 3     2
Run Code Online (Sandbox Code Playgroud)

没有索引,使用GL_TRIANGLES,数组将是这样的:

verts =   [v0, v1, v2,  v2, v3, v0]
normals = [n0, n0, n0,  n0, n0, n0]
colors =  [c0, c0, c0,  c0, c0, c0]
Run Code Online (Sandbox Code Playgroud)

由于顶点和法线通常每个都有3个浮点数,而颜色通常是3个字节,因此对于每个立方体面,它给出:

verts   = 6 * 3 floats = 18 floats
normals = 6 * 3 floats = 18 floats
colors  = 6 * 3 bytes  = 18 bytes

= 36 floats and 18 bytes per cube face.
Run Code Online (Sandbox Code Playgroud)

(我知道如果使用不同的类型,字节数可能会改变,具体数字只是为了说明.)

有了索引,我们可以简化一下,给出:

verts   = [v0, v1, v2, v3]     (4 * 3 = 12 floats)
normals = [n0, n0, n0, n0]     (4 * 3 = 12 floats)
colors  = [c0, c0, c0, c0]     (4 * 3 = 12 bytes)
indices = [0, 1, 2,  2, 3, 0]  (6 shorts)

= 24 floats + 12 bytes, and maybe 6 shorts, per cube face.
Run Code Online (Sandbox Code Playgroud)

看看在后一种情况下,顶点0和2是如何使用两次的,但在每个顶点,法线和颜色数组中只表示一次.这听起来像使用索引的小胜利,即使在每个几何边缘都是接缝的极端情况下也是如此.

这使我得出结论:

3.使用GL_TRIANGLES时,应始终使用索引数组,即使对于所有接缝的几何体也是如此.

如果错误,请以粗体更正我的结论.

Luc*_*uca 33

由此我得出结论,呈现几何时使用时GL_TRIANGLE_STRIP或_FAN,那么我不应该使用索引数组,而是应该使用GL [多图] DrawArrays是所有接缝或大部分接缝.

不,原因很简单.

您的结论是基于您分析了由两个三角形组成的单个四边形的事实.使用三角形扇形/条带绘制的这两个三角形不能使用索引数组进行简化.

但是试着想一下大的地形几何.使用三角形扇形/条形基元将每个地形块绘制为四边形.例如:

图中的每个三角形条带共同具有相邻三角形条带的所有顶点,并且使用索引允许压缩几何体定义,而不是为每个三角形条带重复顶点.


基本上,只要您可以将单个基元的大多数顶点与另一个基元共享,就可以使用索引绘制基本体(三角形,扇形和条形).

共享信息可以节省信息传输带宽,但这并不是唯一的优势.实际上索引数组允许:

  • 避免同步多次指定属于同一"概念"顶点的信息
  • 允许在单个顶点上执行相同的着色器操作,而不是多次执行,每个顶点重复一次.
  • 此外,结合使用三角形条带/扇形和索引允许应用程序压缩索引缓冲区,因为条带/扇形规范需要较少的索引(三角形每个面需要总共3个索引).

正如您指定的那样,只要顶点不能与另一个重合顶点共享与其关联的每个信息(颜色,纹理坐标等),就不能使用索引数组.


仅仅为了完整性,几何规范所需的信息大小不是决定最佳渲染操作的唯一因素.

实际上,原始渲染的另一个基本因素是数据的缓存本地化.指定错误的几何数据(非交错缓冲区对象,长三角形条......)会导致大量缓存未命中,从而降低显卡性能.

为了优化渲染操作,顶点规范应以重新排序的方式重新排序,以最大的概率重用先前指定的顶点.以这种方式,图形卡高速缓存行可以重用先前指定的顶点而无需从存储器中取出它们.

  • +1,但我想通过使用索引添加,然后顶点缓存发挥作用.只要各个三角形之间共享的顶点彼此接近使用,那么图形卡已经知道转换索引"i"的结果是什么,因此将使用缓存的答案而不是重新计算它.如果你有复杂的顶点程序,这非常有用......这也意味着索引的非剥离和非索引剥离之间的顶点变换工作量是相同的.非剥离路径也更加优化,因此您可以获得比剥离更好的性能. (2认同)