什么是顶点阵列对象?

时间:2012-08-06 00:30:58

标签: opengl vbo vertex-array vao

我刚刚从本教程开始学习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信息的目的地吗?

另一方面,我遵循的教程是否可以接受?有什么我应该注意的或者更好的教程可以遵循吗?

4 个答案:

答案 0 :(得分:85)

“顶点数组对象”由OpenGL ARB愚蠢名称小组委员会提供给您。

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

首次绑定VAO时,您可以通过调用

来分配这些成员
glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer...;
glEnableClientState(GL_NORMAL_ARRAY); glNormalPointer...;

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

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

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

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

新创建的VAO的默认值是它为空(AFAIK)。根本没有几何,甚至没有顶点,所以如果你试图绘制它,你将得到一个OpenGL错误。这是合情合理的,如“将所有内容初始化为False / NULL / zero”。

您在设置时只需要glEnableClientState。 VAO记住每个指针的启用/禁用状态。

是的,VAO会存储glEnableVertexAttribArrayglVertexAttrib。旧的顶点,法线,颜色,......数组与属性数组相同,顶点==#0等等。

答案 1 :(得分:7)

顶点数组对象在文字处理程序等中类似于。找到了一个很好的描述here

宏只是记住您所做的操作,例如激活此属性,绑定缓冲区等。当您致电glBindVertexArray( yourVAOId )时,只需重播那些属性指针绑定和缓冲区绑定。

所以你接下来的绘画调用会使用VAO绑定的任何内容。

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

答案 2 :(得分: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 :(得分:4)

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

enter image description here

//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看到:

enter image description here

现在,我们可以使用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)

enter image description here

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 and 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