0
votes

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.

1
Yep, that's what I'm talking about. No idea why I wrote VBA, but it's even all over my code.Acorn

1 Answers

2
votes

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.