Acorn Acorn - 1 month ago 15
C++ Question

VAO not updated when glBindVertexArray

I'm currently learning openGL, and trying to understand the relationship between a VBO and a VAO.

As far as I know, a VBO is a buffer of data and that's all. In order to pass that data into the OpenGL Pipeline, I need to bind that VBO data to a VAO.

This can be achieved via the plethora of

glVertexAttrib*
functions.
Alternatively, you can set the VAO to automatically upload data from a VBO via a set of
glVertexArray*
functions, ending with a call to
glEnableVertexArrayAttrib
.
Now, whenever I change the data in some VBO, all I have to do is call
glBindVertexArray
on my VAO to upload the data from the VBO to the VAO.

Right?

At least, that is how I understand what happens. But, when I try to impliment it in code, I do not see the expected result (a triangle morphing around the screen).

Here is my code for creating the VAO and VBO:

void startup()
{
static const GLfloat positions[] =
{
1.00f, -0.25f, 0.0f, 1.0f,
1.00f, -0.25f, 0.0f, 1.0f ,
1.00f, -0.25f, 0.0f, 1.0f
};


m_renderingProgram = compile_shaders();

//Create VAO and VBO
glCreateVertexArrays(1, &m_VAO);
glCreateBuffers(1, &m_VBO);


//Set the VAO to automatically update it's data with VBO when it is bound
glNamedBufferStorage(m_VBO, sizeof(positions), positions, 0);
glVertexArrayVertexBuffer(m_VAO, 0, m_VBO, 0, 4);
glVertexArrayAttribFormat(m_VAO, 0, 3, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribBinding(m_VAO, 0, 0);
glEnableVertexArrayAttrib(m_VAO, 0);


//Create a named buffer, so we can change the data within easily
glNamedBufferData(m_VBO, sizeof(positions), positions, GL_MAP_WRITE_BIT);

//Upload the data to the VAO
glBindVertexArray(m_VAO);
}


And this is my Update() function code:

void render(double currentTime)
{
const GLfloat red[] = { (float)sin(currentTime) * 0.5f + 0.5f,
(float)cos(currentTime) * 0.5f + 0.5f,
(float)sin(currentTime) * 0.5f + 0.5f,
1.0f };


const GLfloat newPosition[] =
{
sin(currentTime) * 1.00, sin(currentTime) * -0.25, 0.0, 1.0,
tan(currentTime) * 1.00, tan(currentTime) * -0.25, 0.0, 1.0 ,
cos(currentTime) * 1.00, cos(currentTime) * -0.25, 0.0, 1.0
};


const GLfloat black[] = { 0.0f, 0.0f, 0.0f, 1.0f };
glViewport(sin(currentTime) * 0.4f, cos(currentTime) * 0.5f, 800, 600);

glClearBufferfv(GL_COLOR, 0, black);
//Copy the new position to the buffer
glNamedBufferSubData(m_VBO, 0, sizeof(newPosition), newPosition);


//Use the shader program program
glUseProgram(m_renderingProgram);


glVertexAttrib4fv(1, red);
//Update the VAO with the updated VBO
glBindVertexArray(m_VAO);


glDrawArrays(GL_TRIANGLES, 0, 3);
}


What I expect to see is a triangle morphing around the screen and changing colors, but all I see is a static triangle changing colors.

Answer

As far as I know, a VBO is a buffer of data and that's all.

A buffer object is a buffer of data. The term "VBO" refers to a buffer object that is (currently) being used as the source for vertex attributes. Buffer objects can be used for many other things, and you can use the same buffer in multiple different ways.

In order to pass that data into the OpenGL Pipeline, I need to bind that VBO data to a VAO.

The technical term is "attach" the buffer object to a VAO. You "attach" one object to another; you "bind" an object to the context.

Now that we've worked out our terminology, let's talk about where your code goes wrong.

//Create a named buffer, so we can change the data within easily
glNamedBufferData(m_VBO, sizeof(positions), positions, GL_MAP_WRITE_BIT);

You can't do that. You already created storage for that buffer object with glNamedBufferStorage. More importantly, you created immutable storage for it. That means that once you created immutable storage for a buffer object, you cannot go back and create new storage.

Which is fine; indeed, this line is completely superfluous, since you already created the storage and uploaded to it. Just remove this redundant line.

//Copy the new position to the buffer
glNamedBufferSubData(m_VBO, 0, sizeof(newPosition), newPosition);

This is where we start getting into more serious problems.

See, you created the buffer's storage like this:

glNamedBufferStorage(m_VBO, sizeof(positions), positions, 0);

That 0 there is the flags field; it specifies the ways in which you can modify the contents of the storage from the CPU (among other things). That is, immutable storage doesn't mean you can't copy stuff into it again. It simply means you can't reallocate the storage.

But you passed 0. And that means that the buffer you've created is completely static. You told OpenGL that you don't want to change the contents of the buffer via CPU processes. Which is fine for a static buffer, but that's clearly not what you wanted.

So instead of 0, you should have passed an appropriate flag. The flag to allow glNamedBufferSubData to work is called GL_DYNAMIC_STORAGE_BIT.