顶点数组对象-不清楚保存有关当前绑定的顶点缓冲区的确切状态信息

bjp0bcyl  于 2022-09-26  发布在  其他
关注(0)|答案(2)|浏览(129)

我正在学习arcsynthesis上的优秀教程,同时构建了一个图形引擎,并发现我并不像我认为的那样理解VAOS。

来自教程Chapter 5. Objects In Depth
缓冲区绑定和属性关联

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

调用glVertex AttribPointer时,OpenGL会使用绑定到GL_ARRAY_BUFFER的该调用时刻的任何缓冲区,并将其与给定的顶点属性关联。将GL_ARRAY_BUFFER绑定视为glVertex AttribPointer读取的全局指针。因此,在调用glVertex AttribPointer后,您可以自由地将所需的任何内容绑定到GL_ARRAY_BUFFER;这在最终渲染中不会产生任何影响。因此,VAO确实存储哪些缓冲区对象与哪些属性相关联;但它们不存储GL_ARRAY_BUFFER绑定本身。

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

下面列出了该代码的一部分。请注意,我没有在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());

经过一段时间的搜索,并找到了上面遗漏的细节,我发现如果在DRAW函数中先调用glBindBuffer再调用glBufferData,一切都会正常进行。

考虑到这一点,我很困惑为什么我的网格渲染一开始就能正常工作。如果更改缓冲区中的数据,是否只需要再次调用glBindBuffer?或者,如果您不绑定缓冲区,而我只是运气不好,并让它工作,那么行为是否未定义?

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

uoifb46i

uoifb46i1#

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

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

waxmsbnn

waxmsbnn2#

顶点数组对象保存由glEnableVertexAttribArrayglDisableVertexAttribArrayglVertexAttribPointerglVertexAttribIPointerglVertexAttribDivisorgl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER)设置的数据集

换句话说,您可以将Vao定义为

struct VertexAttrib {
  GLint size;           // set by gVertexAttrib(I)Pointer
  GLenum type;          // set by gVertexAttrib(I)Pointer
  GLboolean normalize;  // set by gVertexAttrib(I)Pointer
  GLsizei stride;       // set by gVertexAttrib(I)Pointer
  GLint buffer;         // set by gVertexAttrib(I)Pointer (indirectly)
  void* pointer;        // set by gVertexAttrib(I)Pointer
  GLint divisor;        // set by gVertexAttribDivisor
  GLboolean enabled;    // set by gEnable/DisableVertexAttribArray
};

struct VertexArrayObject {
  std::vector<VertexAttrib> attribs;
  GLuint element_array_buffer;  // set by glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ..)
};

可以使用多少个属性进行查询

GLint num_attribs;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &num_attribs)

您可以认为GL的全局状态具有指向使用glBindVertexArray设置的Vertex ArrayObject的指针

struct GLGlobalState {
  VertexArrayObject default_vao;
  VertexArrayObject* current_vao = &default_vao;
  ...

  GLint current_array_buffer;  // set by glBindBuffer(GL_ARRAY_BUFFER, ...)
}
GLGloalState gl_global_state;

void glBindVertexArray(GLint vao) {
  gl_global_state.current_vao = vao == 0 ? &default_vao : getVAOById(vao);
}

您可以想象上面列出的其他函数是这样工作的

void glEnableVertexAttribArray(GLuint index) { 
  gl_global_state.current_vao->attribs[index].enabled = GL_TRUE;
}

void glEnableVertexAttribArray(GLuint index) { 
  gl_global_state.current_vao->attribs[index].enabled = GL_FALSE;
}

void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized,
                      GLsizei stride, const void* pointer) { 
  VertexAttrib* attrib = &gl_global_state.current_vao->attribs[index];
  attrib->size = size;
  attrib->type = type;
  attrib->normalized = normalized;
  attrib->stride = stride;
  attrib->pointer = pointer;
  attrib->buffer = glGlobalState.current_array_buffer;
}

void glVertexAttribDivisor(GLuint index, GLuint divisor) {
  gl_global_state.current_vao->attribs[index].divisor = divisor;
}

可能更容易从视觉上看到它

this diagram开始,虽然上图显示的是offset而不是pointer,因为它来自不允许客户端数组的WebGL,只有顶点缓冲区,因此指针字段始终被解释为偏移量。

在OpenGL中,如果该属性的buffer字段为0(间接设置,请参见上文),则pointer是指向用户内存的指针。如果属性的buffer非零,则它是缓冲区的偏移量。

有一件事没有存储在VAO中,那就是属性被禁用时的常量值。如果某个属性被禁用(它们默认为禁用,或者您调用gl.disableVertexAttribArray),则该属性将获得常量值。常量值可以用glVertexAttrib???设置。这些值是全球性的。他们不是Vao州的一部分。

相关问题