什么是顶点数组对象?


114

我今天才从本教程开始学习OpenGL:http : //openglbook.com/the-book/
我进入了第2章,在这里画了一个三角形,并且我理解除了VAO以外的所有内容(这个缩写好吗?)。本教程具有以下代码:

glGenVertexArrays(1, &VaoId);
glBindVertexArray(VaoId);

虽然我知道代码是必需的,但我不知道它的作用。尽管在这一点上我从未使用过VaoId(除非要销毁它),但是如果没有它,代码将无法运行。我假设这是因为必须将其绑定,但是我不知道为什么。是否需要将这些确切的代码包含在每个OpenGL程序中?本教程将VAO解释为:

顶点数组对象(或VAO)是描述顶点属性如何存储在顶点缓冲区对象(或VBO)中的对象。这意味着VAO不是存储顶点数据的实际对象,而是顶点数据的描述符。顶点属性可以通过glVertexAttribPointer函数及其两个姊妹函数glVertexAttribIPointer和glVertexAttribLPointer来描述,我们将在下面探讨其中的第一个。

我不了解VAO如何描述顶点属性。我没有以任何方式描述它们。它是否从glVertexAttribPointer获取信息?我想一定是这样。VAO是否仅仅是glVertexAttribPointer提供信息的目的地?

附带说明一下,我遵循的教程是否可以接受?有什么我应该注意的或更好的教程吗?

Answers:


100

傻名称的OpenGL ARB小组委员会为您提供了“顶点数组对象”。

将其视为几何对象。(作为旧的SGI Performer程序员,我称它们为geoset。)对象的实例变量/成员是您的顶点指针,普通指针,颜色指针,attrib N指针,...

首次绑定VAO时,您可以通过调用以下方式分配这些成员

glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer...;
glEnableClientState(GL_NORMAL_ARRAY); glNormalPointer...;

等等。启用了哪些属性,您提供的指针存储在VAO中。

之后,当您再次绑定VAO时,所有这些属性和指针也将变为最新。因此,一次glBindVertexArray调用等效于之前设置所有属性所需的所有代码。在无需创建自己的结构或对象的情况下在函数或方法之间传递几何图形非常方便。

(一次设置,多次使用是使用VAO的最简单方法,但是您也可以仅通过绑定属性并进行更多的启用/指针调用来更改属性。VAO不是常数。)

回答Patrick的问题的更多信息:

新创建的VAO的默认值为空(AFAIK)。根本没有几何图形,甚至没有顶点,因此,如果尝试绘制几何图形,则会出现OpenGL错误。这是相当合理的理智,例如“将所有内容初始化为False / NULL /零”。

glEnableClientState设置时只需要这样做。VAO会记住每个指针的启用/禁用状态。

是的,VAO将存储glEnableVertexAttribArray和保存glVertexAttrib。旧的顶点,法线,颜色,...数组与属性数组,顶点==#0等相同。


62
OpenGL ARB傻名称小组委员会为您带来了“顶点数组对象”。是的,这种存储顶点数组绑定的对象的名字很傻。
尼科尔·波拉斯

2
此外,VAO是否与glVertexAttribPointer
Patrick

2
请为使用核心配置文件的人员添加有关使用通用顶点属性的一些信息。
奥斯卡(Oskar)2012年

8
@NicolBolas一个更好的名字是VertexArrayMacro或类似的名字。
bobobobo

7
@NicolBolas“顶点数组对象”是一个糟糕的名字。它是关于将数据绑定到属性。顾名思义,它与顶点数组无关。名称中没有引用绑定或属性,并且由于“顶点数组”本身是一个独立的概念,因此使理解更加困难。恕我直言,“(顶点)属性绑定对象”更容易理解。甚至“几何对象”也更好:我不喜欢它,但至少它没有过载。
AkiRoss

8

顶点数组对象就像文字处理程序等中的。在这里可以找到很好的描述。

宏仅记住您执行的操作,例如激活该属性,绑定该缓冲区等。调用时glBindVertexArray( yourVAOId ),它仅重播那些属性指针绑定和缓冲区绑定。

因此,您下一次的绘图调用将使用VAO约束的任何内容。

VAO不存储顶点数据。否。顶点数据存储在顶点缓冲区或客户端内存阵列中。


19
-1:它们不像宏。如果是这样,则绑定新的VAO不会禁用以前的VAO启用的顶点数组,除非新的VAO已“记录”您明确禁用了这些数组。像所有 OpenGL对象一样,VAO 保持状态,而不是命令。命令只是更改状态,但是对象带有默认状态集。因此,绑定新创建的VAO将始终禁用所有属性。
Nicol Bolas

6

我一直认为VAO是OpenGL使用的数据缓冲区数组。使用现代OpenGL,您将创建VAO和顶点缓冲区对象。

在此处输入图片说明

//vaoB is a buffer
glGenVertexArrays(1, vaoB); //creates one VAO
glBindVertexArray(vao.get(0));
glGenBuffers(vbo.length, vbo, 0); //vbo is a buffer
glBindVertexArray(vao.get(1));
glGenBuffers(vbo1.length, vbo1, 0); //vbo1 is a buffer
glBindVertexArray(vao.get(2));
glGenBuffers(vbo2.length, vbo2, 0); //vbo2 is a buffer

下一步是将数据绑定到缓冲区:

glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER,vertBuf.limit()*4, vertBuf, GL_STATIC_DRAW); //vertf buf is a floatbuffer of vertices

此时OpenGL看到:

在此处输入图片说明

现在我们可以使用glVertexAttribPointer告诉OpenGL缓冲区中的数据代表什么:

glBindBuffer(GL_ARRAY_BUFFER, 0); //bind VBO at 0
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); //each vertex has 3 components of size GL_FLOAT with 0 stride (space) between them and the first component starts at 0 (start of data)

在此处输入图片说明

OpenGL现在将数据存储在缓冲区中,并且知道如何将数据组织为顶点。可以将相同的过程应用于纹理坐标等,但是对于纹理坐标,将有两个值。

glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER,coordBuf.limit()*4, coordBuf, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0);

接下来,您可以绑定纹理并绘制阵列,您将需要创建Vert和Frag着色器,进行编译并将其附加到程序(此处未提供)。

glActiveTexture(textureID); //bind our texture
glBindTexture(GL_TEXTURE_2D, textureID);
glDrawArrays(GL_TRIANGLES,0,6); //in this case 6 indices are used for two triangles forming a square

5

VAO是代表OpenGL管道的顶点获取阶段的对象,用于将输入提供给顶点着色器。

您可以像这样创建顶点数组对象

GLuint vao;
glCreateVertexArrays(1, &vao);
glBindVertexArray(vao);

首先让我们做一个简单的例子。在着色器代码中考虑这样的输入参数

layout (location = 0) in vec4 offset; // input vertex attribute

要填写此属性,我们可以使用

glVertexAttrib4fv(0, attrib); // updates the value of input attribute 0

尽管顶点数组对象为您存储了这些静态属性值,但它可以做更多的事情。

创建顶点数组对象后,我们可以开始填充其状态。我们将要求OpenGL使用存储在我们提供的缓冲区对象中的数据自动填充它。每个顶点属性都可以从绑定到几个顶点缓冲区绑定之一的缓冲区中获取数据。为此,我们使用glVertexArrayAttribBinding(GLuint vao, GLuint attribindex, GLuint bindingindex)。另外,我们使用glVertexArrayVertexBuffer()函数将缓冲区绑定到顶点缓冲区绑定之一。我们使用该glVertexArrayAttribFormat()函数描述数据的布局和格式,最后通过调用启用属性的自动填充glEnableVertexAttribArray()

当启用顶点属性,OpenGL的将反馈数据,根据您所提供的格式和位置信息的顶点着色器 glVertexArrayVertexBuffer()glVertexArrayAttribFormat()。禁用该属性后,将通过调用向顶点着色器提供您提供的静态信息glVertexAttrib*()

// First, bind a vertex buffer to the VAO
glVertexArrayVertexBuffer(vao, 0, buffer, 0, sizeof(vmath::vec4));

// Now, describe the data to OpenGL, tell it where it is, and turn on automatic
// vertex fetching for the specified attribute
glVertexArrayAttribFormat(vao, 0, 4, GL_FLOAT, GL_FALSE, 0);

glEnableVertexArrayAttrib(vao, 0);

并在着色器中编码

layout (location = 0) in vec4 position;

毕竟,您需要致电glDeleteVertexArrays(1, &vao)


您可以阅读OpenGL SuperBible,以更好地理解它。


3
很高兴看到人们提倡使用DSA风格的OpenGL。
Nicol Bolas
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.