john01dav john01dav - 2 months ago 10
C++ Question

Why can't I create and initialize an OpenGl VAO separately?

I am currently working on an OpenGl 3.3 application that makes use of VAOs (one per mesh). When I create and initialize my VAOs separately, any call to

glDrawElements
causes the program to immediately exit without any error message. When, however, I create and initialize them together, the exact same
glDrawElements
call succeeds.




Separate Initialization Code:

Chunk::Chunk(){
glGenVertexArrays(1, &m_vaoId);
glBindVertexArray(m_vaoId);

glGenBuffers(1, &m_vboId);
glGenBuffers(1, &m_eboId);

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (GLvoid*)(0));
glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (GLvoid*)(1*sizeof(GLfloat)));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
}





void Chunk::refreshMesh(){
/*build data into vector<GLfloat> verticies and vector<GLuint> indices...*/

//send data to GPU
glBindVertexArray(m_vaoId);

glBindBuffer(GL_ARRAY_BUFFER, m_vboId);
glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(GLfloat), vertices.data(), GL_STATIC_DRAW);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_eboId);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size()*sizeof(GLfloat), indices.data(), GL_STATIC_DRAW);
}





Compiled Initialization Code:

void Chunk::refreshMesh(){
/*build data into vector<GLfloat> verticies and vector<GLuint> indices...*/

//send data to GPU
glGenVertexArrays(1, &m_vaoId);
glBindVertexArray(m_vaoId);

glGenBuffers(1, &m_vboId);
glBindBuffer(GL_ARRAY_BUFFER, m_vboId);
glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(GLfloat), vertices.data(), GL_STATIC_DRAW);

glGenBuffers(1, &m_eboId);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_eboId);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size()*sizeof(GLfloat), indices.data(), GL_STATIC_DRAW);

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (GLvoid*)(0));
glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (GLvoid*)(1*sizeof(GLfloat)));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
}





NOTE: I am using Ubuntu 16.04 LTS to compile with g++ 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.2), glew 1.13, and SDL2 2.0.0. I want to do separate initialization to allow for the mesh data to be updated periodically without creating a new VAO, etc. since I imagine a finite number of those can be handled on any real GPU.

Answer

Before VAO's were invented, (almost) everything was saved in the global OpenGL state. This changed with VAO's slightly, as they have their own state.

You are using glVertexAttribPointer to set up the pointers to attributes in buffers, however, the VAO state does not only contain the values you pass to the function but also the buffer which they point to, which is the buffer which was bound when you called the function. This allows for something like this (pseudocode):

bind buffer a;
glVertexAttribPointer(...);
bind buffer b;
glVertexAttribPointer(...);

with this, you'd use attributes from two different buffers. You could then unbind the buffers and forget* about them, you don't need to bind them for drawing.

TLDR: To fix your problem, you need to bind the buffer you intend to use before you call glVertexAttribPointer. You don't need to bind the buffer every time for drawing, as the VAO does not use the currently bound buffer for drawing but the one which was bound when the attribute-pointer was set.

*: To change or delete them you obviously still need their name, hence don't "forget" them in the strict sense of the word.