顶点数组对象 - 关于当前绑定的顶点缓冲区的确切状态信息的确切混淆

时间:2013-06-21 02:59:31

标签: opengl vertex-buffer vertex-array-object

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

来自教程Chapter 5. Objects In Depth

  

缓冲区绑定和属性关联

     

您可能会注意到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,就会保存当前绑定的缓冲区。缺少这些知识,我构建了一个网格类,并且能够获得一个具有正确渲染的多个网格的场景。

下面列出了该代码的一部分。请注意,我不在draw函数中调用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版。

1 个答案:

答案 0 :(得分:11)

  

如果我更改了数据,我只需要再次调用glBindBuffer   缓冲

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