在构建图形引擎的同时,我正在学习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 版。