9

在构建图形引擎的同时,我正在学习arcsynthesis的优秀教程,并发现我并不像我想象的那样了解 VAO。

来自教程第 5 章。深入了解对象

缓冲区绑定和属性关联

您可能会注意到 glBindBuffer(GL_ARRAY_BUFFER) 不在该列表中,即使它是用于渲染的属性设置的一部分。与 GL_ARRAY_BUFFER 的绑定不是 VAO 的一部分,因为在调用 glBindBuffer(GL_ARRAY_BUFFER) 时不会发生缓冲区对象和顶点属性之间的关联。当您调用 glVertexAttribPointer 时,就会发生这种关联。

当您调用 glVertexAttribPointer 时,OpenGL 会获取在此调用时绑定到 GL_ARRAY_BUFFER 的任何缓冲区,并将其与给定的顶点属性相关联。将 GL_ARRAY_BUFFER 绑定视为 glVertexAttribPointer 读取的全局指针。因此,在进行 glVertexAttribPointer 调用后,您可以自由地将任何您想要的内容或任何内容都绑定到 GL_ARRAY_BUFFER;它不会影响最终渲染。所以 VAO 确实存储了哪些缓冲区对象与哪些属性相关联;但他们不存储 GL_ARRAY_BUFFER 绑定本身。

我最初错过了最后一行“......但他们不存储 GL_ARRAY_BUFFER 绑定本身”。在我注意到这一行之前,我认为一旦调用 glVertexAttribPointer 就保存了当前绑定的缓冲区。由于缺少这些知识,我构建了一个网格类,并且能够获得一个具有多个网格正确渲染的场景。

下面列出了该代码的一部分。请注意,我没有在绘图函数中调用 glBindBuffer。

// MESH RENDERING

/* ...            */
/* SETUP FUNCTION */
/* ...            */

// Setup vertex array object
glGenVertexArrays(1, &_vertex_array_object_id);
glBindVertexArray(_vertex_array_object_id);

// Setup vertex buffers  
glGenBuffers(1, &_vertex_buffer_object_id);
glBindBuffer(GL_ARRAY_BUFFER, _vertex_buffer_object_id);
glBufferData(GL_ARRAY_BUFFER, _vertices.size() * sizeof(vertex), &_vertices[0], GL_STATIC_DRAW);

// Setup vertex attributes
glEnableVertexAttribArray(e_aid_position);
glEnableVertexAttribArray(e_aid_normal);
glEnableVertexAttribArray(e_aid_color);
glEnableVertexAttribArray(e_aid_tex);

glVertexAttribPointer(e_aid_position, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)offsetof(vertex, pos));
glVertexAttribPointer(e_aid_normal,   3, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)offsetof(vertex, norm));
glVertexAttribPointer(e_aid_color,    4, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)offsetof(vertex, col));
glVertexAttribPointer(e_aid_tex,      2, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)offsetof(vertex, tex));  

/* ...           */
/* DRAW FUNCTION */
/* ...           */

glBindVertexArray(_vertex_array_object_id);  
glDrawArrays(GL_TRIANGLES, 0, _vertices.size());

现在我要开始照明了,我想绘制一些调试图,这样我就可以验证我所有的法线是否正确。目前,我只是将要为一个帧渲染的所有行存储在一个向量中。由于这些数据可能会在每一帧都发生变化,因此我使用 GL_DYNAMIC_DRAW 并在渲染之前指定数据。

最初,当我这样做时,我会得到指向无穷远的垃圾线。违规代码如下:

// DEBUG DRAW LINE RENDERING 

/* ...            */
/* SETUP FUNCTION */
/* ...            */

// Setup vertex array object
glGenVertexArrays(1, &_vertex_array_object_id);
glBindVertexArray(_vertex_array_object_id);

// Setup vertex buffers  
glGenBuffers(1, &_vertex_buffer_object_id);
glBindBuffer(GL_ARRAY_BUFFER, _vertex_buffer_object_id);
  // Note: no buffer data supplied here!!!

// Setup vertex attributes
glEnableVertexAttribArray(e_aid_position);
glEnableVertexAttribArray(e_aid_color);

glVertexAttribPointer(e_aid_position, 3, GL_FLOAT, GL_FALSE, sizeof(line_vertex), (GLvoid*)offsetof(line_vertex, pos));
glVertexAttribPointer(e_aid_color,    4, GL_FLOAT, GL_FALSE, sizeof(line_vertex), (GLvoid*)offsetof(line_vertex, col));

/* ...           */
/* DRAW FUNCTION */
/* ...           */

glBindVertexArray(_vertex_array_object_id);
  // Specifying buffer data here instead!!!
glBufferData(GL_ARRAY_BUFFER, _line_vertices.size() * sizeof(line_vertex), &_line_vertices[0], GL_DYNAMIC_DRAW);
glDrawArrays(GL_LINES, 0, _line_vertices.size());

经过一番寻找,以及找到我上面遗漏的细节,我发现如果我在绘制函数中调用 glBufferData 之前调用 glBindBuffer,一切正常。

考虑到这一点,我对为什么我的网格渲染首先起作用感到困惑。如果我更改缓冲区中的数据,是否只需要再次调用 glBindBuffer ?或者如果您不绑定缓冲区并且我只是不走运并且让它工作,那么行为是否未定义?

请注意,我的目标是 OpenGL 3.0 版。

4

1 回答 1

12

如果我更改缓冲区中的数据,是否只需要再次调用 glBindBuffer ?

是的,VAO 对象会记住在绑定 VAO 时每次调用glVertexAttribPointer时绑定了哪些缓冲区,因此您通常不需要glBindBuffer再次调用。但是,如果您想更改缓冲区中的数据,OpenGL 需要知道您正在更改哪个缓冲区,因此您需要glBindBuffer在调用glBufferData. 此时绑定哪个 VAO 对象无关紧要。

于 2013-06-21T11:07:44.757 回答