I have a method for initializing my VBO/VAO:
glBindBuffer(GL_ARRAY_BUFFER, mVBO_VertexShader);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexData), indexData, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// setup VAO
glBindVertexArray(mVAO);
glBindBuffer(GL_ARRAY_BUFFER, mVBO_VertexShader);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
And then a separate method for drawing;
glBindVertexArray(mVAO);
glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_SHORT, &mIndexBuffer);
glBindVertexArray(0);
Do I need to unbind the GL_ELEMENT_ARRAY_BUFFER after my call to glDrawElements or is its state 'tied' to the VAO? (So if I unbind the VAO, the GL_ELEMENT_ARRAY_BUFFER will also be unbound)?
GL_ELEMENT_ARRAY_BUFFER is tied to the VAO state. I quote the OpenGL 3.3 spec: "The resulting vertex array object is a new state vector, comprising all the state values listed in tables 6.4 and 6.5."
Table 6.4 contains ELEMENT ARRAY BUFFER BINDING. So no, you do not need to unbind it.
Additionally, you probably want to pass NULL as the last parameter to glDrawElements in your example, since the address is relative to the bound element array buffer..
Bind your vertex array when you set the vertex attrib pointer.
So basically just don't unbind your vertex array as you only have one.
Related
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
[...] 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.
I want to create a VAO for every 3D Object in my program.
I create two VAO's:
//in some method 1
glGenVertexArrays(1, &vao1);
//in some method 2
glGenVertexArrays(1, &vao2);
then for earch VAO I create 2 VBO's:
glBindVertexArray(vao1);
glGenBuffers(2, vbos1);
glBindVertexArray(0);
//and the same for vao2, vbos2
then I send the data to the gpu:
glBindVertexArray(vao1);
glBindBuffer(GL_ARRAY_BUFFER, vbos1[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(positions1), positions1, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbos1[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices1), indices1, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindVertexArray(0);
//and the same for vao2, vbos2, indices2
Then I do the same thing for vao2.
Now only vao2 is drawn.
If I switch the
glGenVertexArrays(1, &vao2);
glGenVertexArrays(1, &vao1);
Then only the vao1 is drawn.
I draw the vaos via:
glBindVertexArray(vao1);
glDrawElements(GL_TRIANGLES, indices1, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
//in another method
glBindVertexArray(vao2);
glDrawElements(GL_TRIANGLES, indices2, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
It makes no difference in which order I draw them, send the data to the gpu or initialize the buffers.
Why is that so?
Right: the order of creating VAOs makes no difference.
Among other things, a VAO stores the buffer binding. You don't need to bind a buffer each time you draw or update data. Just bind the VAO.
If the whole data fits in GPU memory you can update data only once, likely at buffer+VAO creation and binding. If not, then binding the VAO also makes the buffer "associated" in that VAO ready for receiving data.
Can two VAOs (va01, vao2) have the same vertex attribute index number?
GLuint vao1, vao2;
glGenVertexArrays(1, &vao1);
glGenVertexArrays(1, &vao2);
{
glBindVertexArray(vao1);
...
glBindBuffer(GL_ARRAY_BUFFER, vbo1);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
...
glBindVertexArray(0);
}
{
glBindVertexArray(vao2);
...
glBindBuffer(GL_ARRAY_BUFFER, vbo2);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
...
glBindVertexArray(0);
}
Suppose vbo1 and vbo2are defined before these code and they got glBufferDataalready. Can vao1 and vao2 both have the same vertex attribute index number 0?
Yes, several VAOs can set up the same vertex attributes, pointing to different VBOs each.
Suppose vbo1 and vbo2are defined before these code and they got glBufferDataalready. Can vao1 and vao2 both have the same vertex attribute index number 0?
You are confusing some things here. VAOs never care about the BufferData. VAOs store the attribute pointers, the attribute enables, and the GL_ELEMENT_ARRAY_BUFFER_BINDING. They do not store any vertex data, they only reference to it. And they do reference a VBO by name - that means that you can just do:
glBindBuffer(GL_ARRAY_BUFFER, vbo1);
glVertexAttribPointer(i, ...); // here, a reference to vbo1 gets part of the attrib pointer for attrib i
...
glBindBuffer(GL_ARRAY_BUFFER, vbo1);
glBufferData(...); // VAO will now point into this buffer storage
(this also means that you can set up the pointer before you created the buffer storage for the VBO, you just need to have created the VBO object). Maybe you find my illustration in this answer helpful.
I know that VAOs store vertex attribute settings and bound buffer objects, but I'm not sure what is the correct binding-unbinding order I should be doing. Is the next order right?
glBindVertexArray(vao); //bind vao
glBindBuffer(GL_ARRAY_BUFFER, vbo); //bind vbo
glBufferData(GL_ARRAY_BUFFER, sizeof(vec3) * vertices.size(), vertices.data(), GL_STATIC_DRAW);
glVertexAttribPointer(vertex_index, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(vertex_index);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); //bind ebo
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indices.size(), indices.data(), GL_STATIC_DRAW);
glBindVertexArray(0); //unbind vao
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind vbo
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //unbind ebo
At the moment, this is the code I use and it works, but I came to this after many attempts so I want to be sure this is the right way.
GL_ARRAY_BUFFER can be unbound right after glEnableVertexAttribArray/glVertexAttribPointer, since the binding gets stored by these two functions. Unbinding it afterwards is not noted by the VAO anymore.
Since there always exists just one GL_ELEMENT_ARRAY_BUFFER, the VAO stores the last binding state of this buffer type. Thus the index buffer has to be bound up to the moment where the VAO gets unbound.
Your code example is totally valid. You could move the GL_ARRAY_BUFFER unbinding some lines up, but this is not required.
I know the glVertexAttribPointer will use the values from the VBO that was bound when it was called. But can you buffer twice onto the same object? Would it replace what was in? Or can you clear a buffer? I don't know if this approach is correct:
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO); // shared VBO
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(posLoc);
glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 3*sizeof(GLfloat),(GLvoid*)0);
glBufferData(GL_ARRAY_BUFFER, sizeof(colours), colours, GL_STATIC_DRAW);
glEnableVertexAttribArray(colLoc);
glVertexAttribPointer(colLoc, 4, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat),(GLvoid*)0);
glBindBuffer(GL_ARRAY_VERTEX, 0);
glBindVertexArray(0);
Or if I should be using 2 VBOs for buffering the data. What would happen if you call the glBufferData function twice to the same bound vertex array object? This is the other way I would think of for doing this:
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO1); // VBO for vertices
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(posLoc);
glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 3*sizeof(GLfloat),(GLvoid*)0);
glBindBuffer(GL_ARRAY_BUFFER, VBO2); // VBO for colours
glBufferData(GL_ARRAY_BUFFER, sizeof(colours), colours, GL_STATIC_DRAW);
glEnableVertexAttribArray(colLoc);
glVertexAttribPointer(colLoc, 4, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat),(GLvoid*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
The top example won't work as the second glBufferData call will overwrite all of the buffer space in the second one. To properly do that, you have to use the stride and pointer arguments properly, so that the data is interleaved. It's easier (and cleaner imo) to just have multiple VBO's, each storing a separate set of data.