索引究竟是如何工作的?

How exactly does indexing work?

据我了解,OpenGL 中的索引或 IBOs 主要用于减少绘制给定几何体所需的顶点数。我知道使用索引缓冲区,OpenGL 只绘制具有给定索引的顶点并跳过任何其他顶点。但这不是消除了使用纹理的可能性吗?据我所知,如果您跳过带有索引缓冲区的顶点,它也会跳过它们的顶点属性吗?如果我的顶点属性设置如下:

attribute vec4 v_Position;
attribute vec2 v_TexCoord;

然后使用索引缓冲区和glDrawElements(...),这不会消除纹理的使用,还是v_Position得到"reused"?如果他们不这样做,我如何在使用索引缓冲区时进行纹理处理?

索引缓冲区用于提高速度。

使用索引缓冲区,顶点缓存用于存储最近变换的顶点。在转换期间,如果索引指向的顶点已经转换并且在顶点缓存中可用,则重新使用它,否则,顶点被转换。没有索引缓冲区,就无法使用顶点缓存,因此顶点总是会被转换。这就是为什么对索引进行排序以最大化顶点缓存命中率很重要的原因。

索引缓冲区也用于减少内存占用。

单顶点数据通常比较大。例如:存储位置数据(x,y,z)的单精度浮点数需要12个字节(假设每个浮点数需要4个字节)。如果包含顶点颜色、纹理坐标或顶点法线,此内存要求会更大。

如果你有一个由两个三角形组成的四边形,每个顶点仅包含位置数据 (x, y, z)。如果没有索引缓冲区,则需要 6 个顶点(72 字节)来存储四边形。使用 16 位索引缓冲区,您只需要 4 个顶点(48 字节)+ 6 个索引(6*2 字节 = 12 字节)= 60 字节来存储一个四边形。使用索引缓冲区,如果您有许多共享顶点,则此内存要求会变小。

我认为您误解了几个关键术语。

"Vertex attributes" 是定义每个单独顶点的数据。虽然这些包括纹理坐标,但它们还包括 position。事实上,至少如果你不使用固定功能,顶点属性的意义是完全随意的;它们的含义由顶点着色器如何使用 and/or 将它们转发到后续着色器阶段来定义。

因此,将位置、纹理坐标和任何其他顶点属性转发到顶点着色器的方式没有区别。无论哪种方式,它们都完全相同地解析如何使用(或不使用)索引。

顶点着色器示例:

layout(location = 0) in vec4 position;
layout(location = 1) in vec2 uvAttr;

out vec2 uv;

void main( )
{
    uv = uvAttr;
    gl_Position = position;
}

以及上面配对的片段着色器的开头:

in vec2 uv;

如您所见,顶点着色器的输出是基于顶点属性的。然后在将输出发送到片段着色器之前,将该输出插值到由原始组装生成的面。图元装配是索引发挥作用的主要地方:索引决定了如何使用顶点着色器输出来创建实际几何体。然后将该几何体分解成片段,这些片段实际影响渲染输出。顶点着色器的输出成为片段着色器的输入。

在顶点着色器之后,顶点属性停止定义。只有当你像上面那样转发它们时,它们才能被访问以用于纹理之类的东西。因此,您甚至没有首先将顶点属性本身用作纹理坐标:您正在使用顶点着色器输出的变量并在图元 assembly/rasterization.

中进行插值

"if you skip vertices with index buffers, it also skips their vertex attributes"

是 - 它完全忽略顶点:纹理坐标、位置以及您为该顶点定义的任何其他内容。但只有跳过的顶点。其余继续正常处理,就好像跳过的顶点不存在一样。

例如。为了争论起见,让我们说我有 5 个顶点。我已将这些订购成领结形状,如下所示。每个顶点都有位置(只有 x 和 y 的 2 分量向量)和一个用作颜色的分量 "brightness"。领结的中心顶点只定义了一次,但通过索引引用了两次。

顶点属性为:

  1. [(1, 1), 0.5], 又名 [(x, y), 亮度]
  2. [(1, 5), 0.5]
  3. [(3, 3), 0.0]
  4. [(5, 5), 0.5]
  5. [(5, 1), 0.5]

索引为:1、2、3、4、5、3。

请注意,在此示例中,"brightness" 也可以代表您的 UV(W) 坐标。它会被类似地插值,就像一个向量。之前说过,顶点属性的含义是任意的。

现在,既然你问的是跳过顶点,那么如果我将索引更改为 1、2、4,输出将是这样的:

这将是 1、2、3:

看到这里的规律了吗? OpenGL 只关心构成它生成的面的顶点,没有别的。索引只是改变了这些面的组装方式(并且可以使其跳过完全计算不需要的顶点)。它们对所使用的顶点的意义没有影响,并且确实进入了面。如果黑色顶点 #3 被跳过,它对任何面都没有贡献,因为它不是任何面的 part

顺便说一句,该标准允许实现在单个绘制调用中重复使用顶点着色器输出。因此,您应该预料到重复使用相同的索引 可能不会 导致额外的顶点着色器调用。我说 "probably not" 是因为你的驱动程序实际所做的总是巫术。

请注意,在此我有意忽略了曲面细分和几何着色器。这些超出了这个问题的范围,但可能对如何处理顶点属性有一些有趣的影响。我还忽略了一个事实,即顶点的顺序可以在着色器中访问到一定程度,因此可能会影响输出。