OpenGL顶点数组对象(VAO)中存储什么状态?如何正确使用VAO?


25

我想知道OpenGL VAO中存储了什么状态。我知道VAO包含与缓冲顶点的顶点规范有关的状态(缓冲区中包含哪些属性,绑定了哪些缓冲区,...)。为了更好地了解VAO的正确用法,我想确切知道它们的状态。


我如何假设应使用VAO

通过简单的示例,我了解到VAO的正确用法如下:

设定

Generate VAO
BindVAO
---- Specify vertex attributes
---- Generate VBO's
---- BindVBO's
-------- Buffer vertex data in VBO's
---- Unbind VBO's
Unbind VAO

渲染图

Bind VAO
---- Draw
Unbind VAO

据此,我假设至少顶点缓冲区绑定顶点属性规范存储在VAO中。但是我不确定这种使用模式如何扩展到(多个)纹理和(多个)着色器程序起作用的情况。是主动着色器程序存储在VAO?并且纹理绑定(及其采样/包装设置)也存储在VAO中吗?同上制服


因此,我的问题是:

  • OpenGL VAO中存储了什么确切状态?(VBO绑定,属性规范,活动着色器程序,纹理绑定,纹理采样/包装设置,制服...?)
  • 如何在更复杂的渲染设置正确使用VAO,其中包含(多个)纹理以及相关的采样/包装设置,(多个)着色器程序和均匀性?

1
VAO存储有关顶点属性位置的数据。它还存储其中包含这些属性的VBO的ID。绘制对象时不需要绑定VBO,创建VAO时需要在调用glVertexAttribPointer()之前绑定它。
HolyBlackCat 2015年

Answers:


18

VAO存储有关顶点属性位置的数据。(以及其他一些与它们相关的数据。)
"VBO bindings, active shader program, texture bindings, texture sampling/wrapping settings, uniforms"与之完全无关。

您可能会问为什么它不记得VBO绑定。因为不需要绑定VBO来绘制某些东西,所以只需要在创建VAO时将其绑定即可:在调用时glVertexAttribPointer(...),VAO会记住当前绑定的VBO。即使您当前未绑定VAO,VAO也会在绘制时从这些VBO获取属性。


此外,必须稍微不同地使用VAO和VBO:

这行不通

Generate VAO
BindVAO
---- Specify vertex attributes
---- Generate VBO's
---- BindVBO's
-------- Buffer vertex data in VBO's
---- Unbind VBO's
Unbind VAO

因为您需要绑定VBO以指定属性位置。

因此,您应该这样做:

Generate VAO
BindVAO
Generate VBO's
BindVBO's
Specify vertex attributes

您可以随时更改VBO的数据,但必须先绑定。

绘图应如下所示:

Bind VAO
Draw


您可能会注意到,我unbind从您的列表中删除了呼叫。它们几乎完全没有用,并且会稍微降低您的程序速度,因此我认为没有理由调用它们。


9
“所以我没有理由给他们打电话。” 以防止意外更改它们。使用第3方库时尤其是一个问题。
棘轮怪胎2015年

感谢您的好评!简而言之,VAO仅存储顶点属性位置。绑定VAO时,VBO不会反弹,因为VAO知道可以在其中找到属性的缓冲区。所有其他状态都包含在全局OpenGL状态中。
耶勒·范·坎普

@JellevanCampen是的,正确。仅供参考,它还存储属性的开/关状态(gl{Enable|Disable}VertexAttribArray()),默认值(glVertexAttrib*()),实例化模式(glVertexAttribDivisor())以及其他内容。
HolyBlackCat 2015年

@HolyBlackCat您确定默认状态(glVertexAttrib())是VAO状态的一部分吗?OpenGL Wiki另有声明,说它们是上下文状态。
rdb 2015年

@ndb不,我不确定。我希望他们成为VAO国家的一部分,但我没有检查。
HolyBlackCat

4

它仅存储顶点绑定和索引缓冲区绑定

这就是在调用时glVertexAttribPointer绑定到Vertex_Array_buffer的缓冲区加上glVertexAttribPointer绑定的Element_Array_buffer的所有参数。

制服是当前计划的一部分。

其他一切都是全球性的状态。

毫无疑问,您可以检查所使用版本规范中的状态表。


也感谢您的回答!这为我清除了一切。
Jelle van Campen

4

这是一个简单但有效的解释,基本上,一个缓冲区对象具有可以解释为仅原始数据位的信息,它本身没有任何意义,因此可以真正以任何方式查看的数据都是

i.e float vbo[]={1.0,2.0,34.0...}

OpenGL的设计工作方式是必须定义传递给各种着色器的数据看起来像什么

您还必须定义如何读取数据,数据采用的格式,如何处理以及如何使用以及将所有这些信息存储在VAO中

例如,您可以声明存储在像这样的数组中的数据 float vbo = {11.0,2.0,3.0,4.0}

此时,接下来需要的是如何从VAO中的VBO解释该数据,这意味着如下

可以将VAO设置为每个顶点读取2个浮点数(这将使其成为2个具有两个维度x,y的向量),或者您可以告诉vao将其解释为1个具有4个维度的向量,即x,y,z,w等

而且还定义了该数据的其他属性,并将其存储在VAO中,例如数据格式(尽管您声明了float数组,但您可以告诉着色器以整数形式读取它,当然系统会转换原始数据为从浮点数到整数的过程,并有自己的规则集,在这种情况下该怎么做)

所以基本上VBO是数据,而VAO则存储如何解释该数据,因为着色器和OpenGL服务器设计得非常管闲,并且在决定如何处理数据以及如何处理数据以及在何处之前需要了解所有信息放它

当然,它实际上并不复杂,实际上看起来是最高效的,因为它需要将数据存储在图形服务器内存中,以便获得最高效,最快的处理(除非它确定如果不需要,则不需要这样做)数据将不会以这种方式进行处理并用于不经常访问的其他一些信息),因此,为什么需要在VAO中存储有关如何处理数据以及如何处理数据的详细信息,因此VAO就像标头,VBO就像标头使用和定义的纯原始数据(在这种情况下,传递给着色器顶点属性),不同之处在于VBO不仅限于仅由一个VAO使用,可以使用和重用并绑定到许多VAO,例如:

您可以做的是可以将一个缓冲区对象绑定到VAO1,也可以(单独)将同一缓冲区对象绑定到VAO2,每个对象都以不同的方式解释它,以便着色器在哪里处理数据,具体取决于哪个VAO是绑定它将处理与frambuffer不同的相同原始数据(将像素绘制到窗口),导致相同数据的不同显示,这取决于您在VAO中定义的用途


这是最好的答案!
CodingMadeEasy
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.