OpenGL、VAOs和多个缓冲区

30

我正在使用基于OpenGL (通过OpenTK和C#)的小型图形引擎。

为了定义顶点属性,我有一个VertexDeclaration类,包含一个VertexElement结构体数组,并将其映射到glEnableVertexAttribArray/glVertexAttribPointer调用。

此外,为了支持多个顶点流,我有一个特殊的结构来持有顶点缓冲、顶点声明、顶点偏移和实例频率(类似于XNA的VertexBufferBinding结构)。

目前,每当绘制调用被调用时,我遍历所有设置的顶点流,绑定它们的顶点缓冲区,应用顶点声明,禁用未使用的顶点属性并绘制原语。

我想使用VAOs将glEnableVertexAttribArray调用缓存到它们中, 每当应用顶点流时,绑定VAO并更改其数组缓冲区绑定。

这是VAOs的正确用法吗?


似乎这个问题在这里是一个有用的链接。不确定为什么答案对“缓存”glEnableVertexAttribArray调用说“否”。链接问题“When to call glVertexAttribPointer?”的答案表明,这正是VAO的作用,也是现在推荐的方法。 - xealits
1个回答

63
这是正确使用VAO的方式吗?
不是。glVertexAttribPointer使用绑定到GL_ARRAY_BUFFER的缓冲对象,在函数调用时会使用它。因此你不能这样做:
glVertexAttribPointer(...);
glBindBuffer(GL_ARRAY_BUFFER, bufferObject);
glDrawArrays(...);

这将不使用bufferObject;它将使用在glVertexAttribPointer最初调用时绑定到GL_ARRAY_BUFFER的任何内容。
VAO捕获此状态。因此,对于每个顶点属性,VAO将存储在调用时绑定到GL_ARRAY_BUFFER的任何缓冲区对象。这使您可以执行以下操作:
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, buffer1);
glVertexAttribPointer(0, ...);
glVertexAttribPointer(1, ...);
glBindBuffer(GL_ARRAY_BUFFER, buffer2);
glVertexAttribPointer(2, ...);

属性0和1将来自于buffer1,属性2将来自于buffer2。现在VAO捕获了所有状态。要进行渲染,只需执行以下操作:
glBindVertexArray(VAO);
glDraw*();

简而言之,如果您想要改变OpenGL中属性存储的位置,您必须同时改变它的格式。即使是相同的格式,您也必须再次调用glVertexAttribPointer。
1: 这个讨论假设您没有使用新的ARB_vertex_attrib_binding。或者,它被称为"正如Direct3D如何绑定顶点属性"。如果您正在使用提供此扩展的实现,您可以有效地完成您所说的事情,因为属性格式不与缓冲对象的存储绑定。此外,glVertexAttribPointer的复杂逻辑已经消失了。
一般来说,在OpenGL世界中解决这个问题的方法是尽可能将许多东西放在同一个缓冲对象中。如果失败,只需为每个对象使用一个VAO。

谢谢你的出色回答。那么glBufferData步骤在哪里发挥作用呢?在使用glVertexAttribPointer之前,难道不必先缓冲数据吗?抱歉,我对OpenGL非常陌生。 - Ramon Blanquer
我知道这个帖子已经有10年了,但我很惊讶没有其他人指出OP当时使用的是C#,而这个答案中的代码是C++的。 - RWolfe

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