我认为您误解了几个关键术语。
“顶点属性”是定义每个单独顶点的数据。虽然这些包括纹理坐标,但它们也包括位置。事实上,至少如果你不使用固定函数,顶点属性的含义是完全任意的;它们的含义由顶点着色器如何使用和/或将它们转发到后续着色器阶段来定义。
因此,位置、纹理坐标和任何其他顶点属性如何转发到顶点着色器之间没有区别。无论如何使用(或不使用)索引,它们都被完全相同地解析。
一个示例顶点着色器:
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;
如您所见,顶点着色器的输出基于顶点属性。然后在将其发送到片段着色器之前,将该输出插值到由图元组装生成的面上。原始装配是索引发挥作用的主要场所:索引确定如何使用顶点着色器输出来创建实际几何体。然后将该几何体分解为片段,这些片段实际上会影响渲染输出。顶点着色器的输出成为片段着色器的输入。
在顶点着色器之后,顶点属性不再被定义。只有如上所述转发它们,才能访问它们以用于纹理等用途。因此,您甚至没有首先将顶点属性本身用作纹理坐标:您使用的是顶点着色器输出的变量并在原始组装/光栅化中进行插值。
“如果你跳过带有索引缓冲区的顶点,它也会跳过它们的顶点属性”
是的 - 它完全忽略了顶点:纹理坐标、位置以及您为该顶点定义的任何其他内容。但只有跳过的顶点。其余的继续正常处理,就好像跳过的顶点不存在一样。
例如。让我们说为了论证我有 5 个顶点。我将这些订购成蝴蝶结形状,如下所示。每个顶点都有位置(只有 x 和 y 的 2 个分量向量)和一个用作颜色的单个分量“亮度”。领结的中心顶点仅定义一次,但通过索引引用两次。
顶点属性为:
- [(1, 1), 0.5], 又名 [(x, y), 亮度]
- [(1, 5), 0.5]
- [(3, 3), 0.0]
- [(5, 5), 0.5]
- [(5, 1), 0.5]
索引为:1、2、3、4、5、3。
请注意,在此示例中,“亮度”也可以代表您的 UV(W) 坐标。它将被类似地插值,就像一个向量。正如我之前所说,顶点属性的含义是任意的。
现在,由于您要询问是否跳过顶点,因此如果我将索引更改为 1、2、4,则输出如下:
这将是 1、2、3:
看到这里的模式了吗?OpenGL 只关心构成它生成的面的顶点,仅此而已。索引仅仅改变了这些面的组合方式(并且可以使它跳过完全计算的不需要的顶点)。它们对所使用的顶点的含义没有影响,并且确实进入了面。如果黑色顶点#3 被跳过,它不会对任何面产生影响,因为它不是任何面的一部分。
顺便说一句,该标准允许实现在单个绘制调用中重用顶点着色器输出。因此,您应该期望重复使用相同的索引可能不会导致额外的顶点着色器调用。我说“可能不是”,因为你的司机实际上做的总是巫毒。
请注意,在此我有意忽略了曲面细分和几何着色器。这些是超出此问题范围的主题,但可能对如何处理顶点属性有一些有趣的影响。我还忽略了这样一个事实,即顶点的顺序可以在一定程度上在着色器中访问,因此可能会影响输出。