Search code examples
c++openglglfwvaovertex-array-object

OpenGL can't update vertex buffer with VAO procedure


I'm attempting to move a vertex by modifying it's positional vertex attribute. As a test, I have added the line vertices[0] = 0.4f; both before and after the creation of my VAO procedure, to see whether I am able to modify the vertices array after an initial render. When I add it before the VAO creation, it modifies the location of the vertex, and when it is added afterwards it does not. This leads me to believe my rendering procedure:

glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);

glfwSwapBuffers(window);
glfwPollEvents();

Somehow isn't actually updating the buffer with the current float[] in memory. However, I can then replace the line glBindVertexArray(VAO); with the whole rendering procedure:

    // 2. copy our vertices array in a buffer for OpenGL to use
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
    // 2. copy our vertex indices in a buffer for OpenGL to use
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(indices), indices);
    // 3. then set our vertex attributes pointers:
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void*)0); // Pos vec3
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void*)(3* sizeof(float))); // Col vec4
    glEnableVertexAttribArray(1);

And with this as my rendering procedure, I can update the vertices array and this change is carried across to the GPU, updating the position of the vertex on-screen. Here is my VAO creation code:

    // Generate a Vertex Array Object to store our rendering procedure.
    unsigned int VAO;
    glGenVertexArrays(1, &VAO);
    // 1. bind Vertex Array Object, Element Buffer Object
    glBindVertexArray(VAO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    // 2. copy our vertices array in a buffer for OpenGL to use
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
    // 2. copy our vertex indices in a buffer for OpenGL to use
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(indices), indices);
    // 3. then set our vertex attributes pointers:
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void*)0); // Pos vec3
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void*)(3* sizeof(float))); // Col vec4
    glEnableVertexAttribArray(1);

So it works when I express the rendering procedure explicitly, but not when I store it in a VAO? From what I understand, a VAO is a construct for storing a rendering procedure, and when we then run glBindVertexArray(VAO);, this rendering procedure is carried out. Am I understanding this wrong? Is there another line I need in either the creation of the VAO or when rendering?

Here is the full source in C++: https://pastebin.com/DgZuZt4K

And the same thing written in OpenTK, C#: https://pastebin.com/DHj9UN16


Solution

  • [...] From what I understand, a VAO is a construct for storing a rendering procedure, [...]

    No it is not. A Vertex Array Object stores states.

    When glBufferData/glBufferSubData is called, then the buffer object's data store is initialized respectively updated.

    When glVertexAttribPointer is called, then states are set in the state vector of the VAO. The buffer object which is currently bound to the target GL_ARRAY_BUFFER is associated to the specified vertex attribute in the currently bound VAO.
    The VAO stores the information about how to interpret the vertex information and the id of the VBO for each attribute.

    But the VAO does not store a procedure or even a process. When you have changed the vertices, then you have to update the Vertex Buffer Object by either glBufferSubData or buffer mapping.

    Related: What is the proper way to modify OpenGL vertex buffer?


    If you want to specify and enable a vertex attribute (glVertexAttribPointer / glEnableVertexAttribArray), then you have to bind the VAO and the VBO. The VAO stores the specification and the id of the VBO is stored in the VAO.

    If you want to update the vertex coordinates and attributes (e.g. glBufferSubData), then you have to bind the VBO.

    If you want to draw the mesh (e.g. glDrawArrays), then it is sufficient to bind the VAO.