在OpenGL 3.x中使用索引顶点数组时,我如何指定每个面的颜色?

28
我正在尝试使用包含8个顶点和1个24(4*6)索引的索引数组渲染一个立方体。但是如果我想要指定每个面(per-face)的变量,比如颜色和法线,而不使用废弃的函数,该怎么办呢?为此我需要一个单独的索引集,但是当我指定两个索引数组(GL_ELEMENT_ARRAY_BUFFERs),并将它们指向不同的着色器变量(通过两次调用glVertexAttribPointer),就会出现问题,它不会渲染任何东西(但也不报告任何错误 - 使用glGetError进行了检查)。我是否必须对每个面使用不同的glDrawElements调用,并将颜色和法线加载到统一变量中?
为了澄清,当每个顶点属于不同的面并且需要不同的颜色和法线值时,才会出现问题。
5个回答

45

有关使用数组索引还是不使用的问题,对于OpenGL调用的不同行为,我创建了一张图表以图形方式进行总结,这样您就可以看到哪些调用会绘制连续的顶点数据块,而哪些调用会使用索引进行“跳跃”。

OpenGL draw calls

我根据 此来源 的 Creative Commons BY-SA 许可发布了这个图像。


这是一张不错的图片。能否指定一个许可证? - Bahbar
当然。有没有一个正式声明的好方法,或者我只是在评论中宣布它?我不熟悉为图像选择许可证,但很乐意尽可能地开放。是否有适当的自由CC许可证?(欢迎其他建议) - Jonathan Hartley
如果您想使用CC(知识共享)许可证,可以在这里使用一个很好的工具进行选择:http://creativecommons.org/choose/。图像是其中可选的格式之一。否则,如果您不关心版权保留,可以直接声明为公共领域。在所有情况下,您都应该在您的网站上明确指定。 (不用说,我不是法律专家,但是嘿,我尽力关注) - Bahbar
+1 这是一个非常好的图表,真正澄清了绘制调用。 - Christian Rau

33

实际答案如下:
请参阅Goz的答案。保留24个单独的顶点。

术语解释如下:
顶点是一组顶点属性。阅读以下内容时,请记住保持这种区分:

您误认为使用已弃用的API将帮助您解决问题。事实并非如此。OpenGL处理(并始终处理)每个顶点作为唯一的一组属性。如果仔细阅读原始规格说明,您会注意到在执行以下操作时:

glNormal()
glVertex()
glVertex()
glVertex()

规范明确指出,glNormal设置当前法线状态,而glVertex触发一个新顶点,同时复制所有当前状态,包括当前法线状态。也就是说,即使你只传递了一个法线,GL仍然会看到3个。
因此,GL没有"面内"属性。
此外,您正在混合索引数组GL_ELEMENT_ARRAY_BUFFER和顶点属性数组GL_ARRAY_BUFFER。前者从glDrawElements(...,pointer)使用,其中pointer是索引数组内的偏移量;后者从glVertexAttribPointer(以及所有已弃用的glVertexPointer / glNormalPointer ...)使用。
索引缓冲区中的每个索引都将用作每个属性的索引,但您只能为每个顶点指定单个索引。因此,设置GL_ELEMENT_ARRAY_BUFFER,然后调用glVertexAttribPointer根本不会达到您想要的效果。它要么使用您最后设置为GL_ARRAY_BUFFER的数组来定义顶点属性,要么(如果您没有保持绑定)将解释您的偏移量作为指针(并可能崩溃)。
您尝试的设置每个顶点属性的索引数组不受GL支持。让我重申一下:每次绘制只有1个索引数组
对于历史爱好者,这里还有一些额外的小贴士:

glVertex有点名不副实。它只指定了顶点的位置。但是,正如它得名的原因,它还会引发一个顶点被传递到GL。为了使API完全干净,您可以想象需要进行2个调用:

// not valid code
glPosition(1,2,3); // specifies the current vertex position
glProvoke(); // pass the current vertex to GL

然而,当GL首次被指定时,位置(Position)始终是必需的,因此将这两者融合以引发一个顶点是有意义的(即使只是为了减少API调用计数)。
快进到vertex_program_arb:试图摆脱固定功能模型,同时仍保持兼容性意味着必须沿用glVertex的特殊性质。这通过将顶点属性0设置为触发器并成为glVertex的同义词来实现。
快进到GL3.2:开始/结束模型已经消失,所有引发顶点的规范都可以最终消失,连同当前状态的管理一起消失。由于所有输入现在都是顶点属性,因此所有语义API(如glVertex*,glNormal*...)也可以消失。

再次感谢您,Bahbar,提供非常详尽的答案 :) 但是现在我不理解索引缓冲区的意义...《红书》(第七版)让我相信它的作用是为了容纳在模型中出现多次的重复顶点位置。它甚至使用立方体作为例子,指出“每个顶点恰好被三个面使用”。当然,在示例中他们没有提到法向量/颜色... - Wonko
1
通常,具有不同法线的单个顶点位置是接缝的标志。就大多数网格而言,这些顶点是例外而不是规范。大多数应用程序从使用索引中受益匪浅,因为在光滑表面上的所有属性都保持共享。关于 .obj 文件... 好吧,它们是为 3ds max 创建的,而不是为运行时渲染创建的。您必须对它们进行一些处理才能将它们与任何图形 API 一起使用。在 GL 中无法获得此行为,因为那不是 硬件 的工作方式。 - Bahbar
1
在巴赫巴尔的解释中再加一点,一个立方体可能是最不适合展现索引缓冲区好处的模型,因为每条边都是缝隙。 - Simon H.
嘿。如果我绘制的模型中几乎每个边缘都是接缝线(例如数千个立方体的组合),那么使用索引是否仍然有价值?我应该通过测量性能来确定它的实际效果吗?感谢任何想法,并为necromancer子查询道歉。 - Jonathan Hartley
啊!我意识到这个问题比我想象的更加棘手。我会提出一个新的问题来搞清楚。 - Jonathan Hartley
显示剩余2条评论

7
有趣的解释,但它们似乎掩盖了原始问题包含自己答案的事实:“我是否需要为每个面使用不同的glDrawElements调用,并将颜色和法线加载到统一变量中?” - 如果必须将相同的顶点位置数据馈送三次到图形处理器中,而只需一次就足够,这会冒犯美感(应该),那么答案是肯定的。
原始提问者并不想知道OpenGL不能做他试图做的事情,以及他需要维护24个立方体顶点,而标准数学和常识告诉我们只需要8个。他已经知道他可以做他想做的事情,甚至知道如何做。他只是想确认他提出的方法确实有意义,并且没有忽视他的其他更好的方法。
为每个面制作调用确实会增加其自身的开销,但在仅绘制一个立方体时,这并不重要。当绘制许多相同属性的面时,可以将它们批处理在一起,例如所有朝南的面然后所有朝西的面,或所有红色然后所有绿色等。

6

您需要多于8个顶点。 顶点可能共享位置,但除非顶点中的其他所有内容都是唯一的,否则它不是唯一的顶点。法线是需要在立方体定义中使用多于8个顶点的另一个常见原因。


2
在OpenGL 3.x中,您可以访问插值限定符。使用flat限定符,您可以强制使用仅由引发顶点传递的变量。在某些情况下,这应该足以实现您想要的效果,但它的缺点是您需要非常注意绘制顶点的顺序。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接