PDA

View Full Version : Multiple VBOs in one VAO



Hermannicus
04-24-2016, 11:15 AM
Hi,

I'm just learning OpenGL and try to draw multiple objects and use multiple vertex and index buffers for that. If I use different VAOs it works fine, but with only one I don't get it working. So is every VAO meant to hold only one buffer of each type or am I doing something wrong?

This works:

//Creating:
GLuint vao[2], vbo[4];
glGenVertexArrays(2, vao);
glGenBuffers(4, vbo);

glBindVertexArray(vao[0]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(bottom), bottom, GL_STATIC_DRAW);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(bottomElements), bottomElements, GL_STATIC_DRAW);

glBindVertexArray(vao[1]);

glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
glBufferData(GL_ARRAY_BUFFER, sizeof(cube), cube, GL_STATIC_DRAW);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cubeElements), cubeElements, GL_STATIC_DRAW);

//Drawing:
glBindVertexArray(vao[0]);
glDrawElements(GL_TRIANGLES, sizeof(bottomElements)/sizeof(GLushort), GL_UNSIGNED_SHORT, 0);
glBindVertexArray(vao[1]);
glDrawElements(GL_TRIANGLES, sizeof(cubeElements)/sizeof(GLushort), GL_UNSIGNED_SHORT, 0);

This doesn't work. It uses both times the buffers I created last:

//Creating:
GLuint vao, vbo[4];
glGenVertexArrays(1, &vao);
glGenBuffers(4, vbo);

glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(bottom), bottom, GL_STATIC_DRAW);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(bottomElements), bottomElements, GL_STATIC_DRAW);

glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
glBufferData(GL_ARRAY_BUFFER, sizeof(cube), cube, GL_STATIC_DRAW);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cubeElements), cubeElements, GL_STATIC_DRAW);

//Drawing:
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[1]);
glDrawElements(GL_TRIANGLES, sizeof(bottomElements)/sizeof(GLushort), GL_UNSIGNED_SHORT, 0);
glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
glDrawElements(GL_TRIANGLES, sizeof(cubeElements)/sizeof(GLushort), GL_UNSIGNED_SHORT, 0);

GClements
04-24-2016, 05:08 PM
Hardly any of the code you show is related to VAO state. The only calls which actually affect VAO state are the glBindBuffer(GL_ELEMENT_ARRAY_BUFFER) calls.

The state contained in a VAO consists of:

1. For each attribute:
- whether it is enabled (glEnableVertexAttribArray/glDisableVertexAttribArray)
- the size, type, stride, normalised flag, integer flag, long flag, and offset ("pointer") (glVertexAttribPointer)
- the buffer object containing the data (i.e. the buffer which was bound to GL_ARRAY_BUFFER at the time of the glVertexAttribPointer call).
- the attribute divisor (glVertexAttribDivisor)

2. The buffer currently bound to GL_ELEMENT_ARRAY_BUFFER.

[There is actually more state related to vertex buffer bindings, but that isn't relevant here and has been omitted for simplicity.]

Note that the current GL_ARRAY_BUFFER binding isn't stored in a VAO. You don't need to bind a VAO before binding a buffer to that target or modifying the buffer's data. You do need to bind a VAO before calling any of the functions listed in 1 or 2 above which modify the VAO state, and before calling drawing functions (glDrawElements etc). You do need to bind the correct buffer to GL_ARRAY_BUFFER before calling glVertexAttribPointer, as that function stores the current binding in the VAO along with its parameters.

Hermannicus
04-25-2016, 01:46 AM
Ah ok, thank you. That helped.

So I have to call glVertexAttribPointer every time when using other buffers and specify their layout again. I could not just say like "Here are new buffers with the same layout as the ones before"?

So I have two working versions right now. The one with the two VAOs and this one:


glBindVertexArray(vao);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, buffer[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(bottom), bottom, GL_STATIC_DRAW);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(bottomElements), bottomElements, GL_STATIC_DRAW);

glBindBuffer(GL_ARRAY_BUFFER, buffer[2]);
glBufferData(GL_ARRAY_BUFFER, sizeof(cube), cube, GL_STATIC_DRAW);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer[3]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cubeElements), cubeElements, GL_STATIC_DRAW);

mainloop{
glBindBuffer(GL_ARRAY_BUFFER, buffer[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer[1]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), BUFFER_OFFSET(0));
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), BUFFER_OFFSET(3));
glDrawElements(GL_TRIANGLES, sizeof(bottomElements)/sizeof(GLushort), GL_UNSIGNED_SHORT, 0);

glBindBuffer(GL_ARRAY_BUFFER, buffer[2]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer[3]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), BUFFER_OFFSET(0));
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), BUFFER_OFFSET(3));
glDrawElements(GL_TRIANGLES, sizeof(cubeElements)/sizeof(GLushort), GL_UNSIGNED_SHORT, 0);
}

I guess that the one with the two VAOs would have the better performance and the one intended by the creators of OpenGL, right? Sorry still struggling a bit with the concepts.

GClements
04-25-2016, 07:47 AM
So I have to call glVertexAttribPointer every time when using other buffers and specify their layout again. I could not just say like "Here are new buffers with the same layout as the ones before"?

It depends upon what changes and when.

For changing the attribute data which is used for rendering, there are three basic options:

1. Bind a different VAO which references different buffers (glBindVertexArray).
2. Change the buffers which the current VAO references (glBindBuffer + glVertexAttribPointer).
3. Change the data in the existing buffers (glBufferSubData, glMapBuffer etc).

If you're using OpenGL 4.3 or later, there are some more options, which are basically what was "omitted for clarity" from my previous post.

4. Change the buffer associated with a particular binding point (glBindVertexBuffer).
5. Change the binding point associated with a particular attribute (glVertexAttribBinding).

OpenGL 4.3 incorporates the ARB_vertex_attrib_binding extension which adds an extra level of indirection (binding points) between attributes and buffers. Each attribute is associated with a binding point and each binding point is associated with a buffer. The format of an attribute array can be specified independently of the binding point and buffer using glVertexAttribFormat. The attribute is associated with a binding point via glVertexAttribBinding, and the binding point with a buffer via glBindVertexBuffer. glVertexAttribPointer is equivalent to calling all three functions. All of this state (in addition to that described previously) is stored in the VAO.

In this model, the stride and instance divisor (along with the buffer) are part of the state associated with the binding point rather than the attribute. Both the attribute and the binding point have separate offsets, and the offset into the buffer is the sum of these.


I guess that the one with the two VAOs would have the better performance and the one intended by the creators of OpenGL, right? Sorry still struggling a bit with the concepts.
If you have a (relatively small) finite set of combinations of attribute state, creating a VAO for each combination and switching between them is likely to be more efficient than using a single VAO and continually changing much of its state. OTOH, if the state for different attributes is largely independent, the number of VAOs required for this approach would tend to grow exponentially, which makes it impractical.