132

我今天刚开始从本教程中学习 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

4 回答 4

114

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

把它想象成一个几何对象。(作为一个旧时代的 SGI Performer 程序员,我称它们为 geosets。)对象的实例变量/成员是您的顶点指针、普通指针、颜色指针、属性 N 指针,...

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

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

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

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

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

更多信息以回应帕特里克的问题:

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

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

是的,VAO 将存储glEnableVertexAttribArrayglVertexAttrib。旧的vertex, normal, color, ...数组和属性数组一样,vertex == #0等等。

于 2012-08-06T01:24:45.740 回答
23

我一直认为 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
于 2019-07-29T17:14:57.153 回答
11

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

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

因此,您的下一个绘图调用将使用 VAO 绑定的任何内容。

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

于 2013-07-07T22:39:56.693 回答
7

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以更好地理解它。

于 2015-10-14T08:17:18.603 回答