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

发布于 2024-09-03 10:44:27 字数 2384 浏览 8 评论 0原文

我试图清楚地了解何时应该使用用 gl[Multi]DrawElements 等绘制的 OpenGL 顶点索引数组,以及何时应该简单地使用用 gl[Multi]DrawArrays 绘制的连续顶点数组。

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

我已经在这个问题上反复讨论了好几次,所以我将概述我目前的理解,希望有人能告诉我我现在或多或少是正确的,或者指出我剩余的误解在哪里。具体来说,我有三个结论,用粗体表示。如有错误,请指正。

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

这使我得出结论:

1。对于接缝很少的几何图形,索引数组是一个巨大的胜利。

始终遵循规则 1,除了:

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

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

 0     1
  o---o
  |\  |
  | \ |
  |  \|
  o---o
 3     2

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

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

verts  = [v0, v1, v2, v3]
colors = [c0, c0, c0, c0]
normal = [n0, n0, n0, n0]

添加索引不允许我们简化它。

由此我得出的结论是:

2。当渲染全部接缝或大部分接缝的几何体时,当使用 GL_TRIANGLE_STRIP 或 _FAN 时,我不应该使用索引数组,而应该始终使用 gl[Multi]DrawArrays。

更新:回复表明这个结论是错误的。尽管索引不允许我们减少这里数组的大小,但由于其他性能优势,仍然应该使用它们,如评论中所述)

规则 2 的唯一例外是:

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

 0     1
  o---o
  |\  |
  | \ |
  |  \|
  o---o
 3     2

如果没有索引,使用 GL_TRIANGLES,数组将类似于:

verts =   [v0, v1, v2,  v2, v3, v0]
normals = [n0, n0, n0,  n0, n0, n0]
colors =  [c0, c0, c0,  c0, c0, c0]

由于顶点和法线通常各为 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.

我知道如果使用不同的类型,字节数可能会改变,确切的数字仅用于说明。)

使用索引,我们可以稍微简化一下,给出:

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.

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

这让我得出结论:

3。使用 GL_TRIANGLES 时,应该始终使用索引数组,即使对于全是接缝的几何图形也是如此。

如果我的结论有误,请以粗体字更正。

I'm trying to get a clear idea of when I should be using indexed arrays of OpenGL vertices, drawn with gl[Multi]DrawElements and the like, versus when I should simply use contiguous arrays of vertices, drawn with gl[Multi]DrawArrays.

(Update: The consensus in the replies I got is that one should always be using indexed vertices.)

I have gone back and forth on this issue several times, so I'm going to outline my current understanding, in the hopes someone can either tell me I'm now finally more or less correct, or else point out where my remaining misunderstandings are. Specifically, I have three conclusions, in bold. Please correct them if they are wrong.

One simple case is if my geometry consists of meshes to form curved surfaces. In this case, the vertices in the middle of the mesh will have identical attributes (position, normal, color, texture coord, etc) for every triangle which uses the vertex.

This leads me to conclude that:

1. For geometry with few seams, indexed arrays are a big win.

Follow rule 1 always, except:

For geometry that is very 'blocky', in which every edge represents a seam, the benefit of indexed arrays is less obvious. To take a simple cube as an example, although each vertex is used in three different faces, we can't share vertices between them, because for a single vertex, the surface normals (and possible other things, like color and texture co-ord) will differ on each face. Hence we need to explicitly introduce redundant vertex positions into our array, so that the same position can be used several times with different normals, etc. This means that indexed arrays are of less use.

e.g. When rendering a single face of a cube:

 0     1
  o---o
  |\  |
  | \ |
  |  \|
  o---o
 3     2

(this can be considered in isolation, because the seams between this face and all adjacent faces mean than none of these vertices can be shared between faces)

if rendering using GL_TRIANGLE_FAN (or _STRIP), then each face of the cube can be rendered thus:

verts  = [v0, v1, v2, v3]
colors = [c0, c0, c0, c0]
normal = [n0, n0, n0, n0]

Adding indices does not allow us to simplify this.

From this I conclude that:

2. When rendering geometry which is all seams or mostly seams, when using GL_TRIANGLE_STRIP or _FAN, then I should never use indexed arrays, and should instead always use gl[Multi]DrawArrays.

(Update: Replies indicate that this conclusion is wrong. Even though indices don't allow us to reduce the size of the arrays here, they should still be used because of other performance benefits, as discussed in the comments)

The only exception to rule 2 is:

When using GL_TRIANGLES (instead of strips or fans), then half of the vertices can still be re-used twice, with identical normals and colors, etc, because each cube face is rendered as two separate triangles. Again, for the same single cube face:

 0     1
  o---o
  |\  |
  | \ |
  |  \|
  o---o
 3     2

Without indices, using GL_TRIANGLES, the arrays would be something like:

verts =   [v0, v1, v2,  v2, v3, v0]
normals = [n0, n0, n0,  n0, n0, n0]
colors =  [c0, c0, c0,  c0, c0, c0]

Since a vertex and a normal are often 3 floats each, and a color is often 3 bytes, that gives, for each cube face, about:

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.

(I understand the number of bytes might change if different types are used, the exact figures are just for illustration.)

With indices, we can simplify this a little, giving:

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.

See how in the latter case, vertices 0 and 2 are used twice, but only represented once in each of the verts, normals and colors arrays. This sounds like a small win for using indices, even in the extreme case of every single geometry edge being a seam.

This leads me to conclude that:

3. When using GL_TRIANGLES, one should always use indexed arrays, even for geometry which is all seams.

Please correct my conclusions in bold if they are wrong.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

海未深 2024-09-10 10:44:27

由此我得出的结论是,当渲染全部是接缝或主要是接缝的几何体时,当使用 GL_TRIANGLE_STRIP 或 _FAN 时,我不应该使用索引数组,而应该始终使用 gl[Multi]DrawArrays。

不,原因很简单。

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

但尝试考虑一个大的地形几何形状。每个地形块都使用三角形扇形/条带基元绘制为四边形。例如:

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


基本上,当您可以与另一个图元共享单个图元的大部分顶点时,使用索引绘制图元(三角形、扇形和条形)非常有用。

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

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

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


只是为了完整起见,几何规范所需的信息的大小并不是确定最佳渲染操作的唯一因素。

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

为了优化渲染操作,顶点规范应以最大概率重用先前指定的顶点的方式重新排序。通过这种方式,显卡缓存行可以重用先前指定的顶点,而无需从内存中获取它们。

From this I conclude that when rendering geometry which is all seams or mostly seams, when using GL_TRIANGLE_STRIP or _FAN, then I should never use indexed arrays, and should instead always use gl[Multi]DrawArrays.

No, and the reason is quite simple.

Your conclusion is based on the fact you have analysed a single quad composed by two triangles. These two triangles drawn using triangle fan/strip cannot be simplified using indexed arrays.

But try to think about a large terrain geometry. Each terrain block is drawn as a quad, using triangle fan/strip primitive. For example:

Each triangle strip in the figure has in common all vertices with adjacent triangle strips, and using indices allow to compress the geometry definition, instead of repeating vertices for each triangle strip.


Basically, drawing primitives (triangles, fans and strips) using indices are usefull whenever you can share most of vertices of a single primitive with another one.

Sharing the information allow to save information transmission bandwidth, but it is not the only advantage. Actually indexed arrays allow:

  • Avoid the synchronization of the information belonging to the same "conceptual" vertex, specified many times
  • Allow to perform the same shader operation on a single vertex instead executing many times, one for each vertex duplication.
  • Furthermore, combining the use of triangle strips/fans and indices allow the application to compress the indices buffer, since the strip/fan specification requires less indices (a triangle requires always 3 indices for each face).

Indexed array cannot be used, as you have specified, whenever a vertex cannot share every information associated with it (color, texture coords and so on) with another coincident vertex.


Just for the sake of completeness, the size of the information necessary for the geometry specification is not the only factor wich determine the optimal rendering operation.

Infact, another fundamental factor for primitive rendering is the cache localization of the data. Badly specified geometry data (non interleaved buffer objects, long triangle strips...) causes a lot of cache misses, degrading graphic card performance.

In order to optimize the rendering operation, the vertex specification shall be reordered in a way to reuse previously specified vertices, with the most probability. In such way, the graphic card cache line can reuse previously specified vertices without fetching them from memory.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文