Sunday, September 1, 2019

OpenGL: Is it possible to use VAO's without specifying a VBO


On all the tutorials I can find about VAO's (Vertex Array Objects), they show on how to use them by configuring vertex attributes and binding a VBO (Vertex Buffer Object). But I want to create a VAO that will be configured for a set of VBO's in combination with a fixed shader, where each buffer uses the same data pattern (vertex, uv, color, etc). So, I want to create one VAO for multiple VBO's that will be drawn using one shader.


I couldn't find any demo on this, so I decided to just give it a try. But it doesn't work and crashes on the glDrawArray call. It looks like the VBO isn't bound. Here is the code I'm using:


Rendering:


/* Prepare vertex attributes */
glBindVertexArrayOES(vao);

/* Upload the buffer to the GPU */
glBindBuffer(GL_ARRAY_BUFFER, pool->next());
glBufferSubData(GL_ARRAY_BUFFER, 0, parts * buffer.stride() * 6, buffer.getBuffer());


/* Draw the triangles */
glDrawArrays(GL_TRIANGLES, 0, parts * 6);

glBindVertexArrayOES(0);

VAO Creation:


glBindVertexArrayOES(vao);

glEnableVertexAttribArray(ls.position);

glVertexAttribPointer(ls.position, 2, GL_FLOAT, GL_FALSE, buffer.stride(), 0);

glEnableVertexAttribArray(ls.color);
glVertexAttribPointer(ls.color, 3, GL_FLOAT, GL_FALSE, buffer.stride(), GL_BUFFER_OFFSET(buffer.dataTypeSize * 2));

glBindVertexArrayOES(0);

Where ls is a simple struct that holds the attribute locations.


In the Rendering part, swapping the glBindBuffer and the glBindVertexArrayOES around didn't work either.


So, the question is: Is it even possible to do so, or will I have to create for each buffer a VAO? And if I have to create a VAO for each VBO, is it possible to update the VBO's data using glBufferSubData in combination with a VAO?




Answer



VAOs do not contain "glBindBuffer" state, with the exception of GL_ELEMENT_ARRAY_BUFFER's binding state. What you're not understanding is that glBindBuffer(GL_ARRAY_BUFFER) doesn't do anything. Well, it doesn't do anything as far as rendering is concerned. Try it; right before calling glDraw*, call glBindBuffer(GL_ARRAY_BUFFER, 0); your rendering will work just fine.


The way glVertexAttribPointer works is that it looks at what is bound to GL_ARRAY_BUFFER at the time glVertexAttribPointer is called. Not at render time. Not later. Right at that exact moment. It takes whatever buffer object was there and stores it in another piece of OpenGL state, which is encapsulated within the VAO.


In general, you have two options, one of them that's new and probably shouldn't be used at this point in time.


Your first option is to put all objects that use the same vertex format (ie: everything except the buffer object and offset) in the same buffer object. Basically, build a giant array that contains all the vertices for all object that use the same format.


When it comes time to render, you can render a portion of the vertices. glDrawArrays takes a range of elements to render, and you can adjust your indices in glDrawElements to do the same. Alternatively, you can use glDrawElementsBaseVertex to bias the vertices, so that an offset is added to each index. This offset is the number of vertices before that vertex in the big array.


The other alternative is to use the relatively new format attribute separation system added in GL 4.3. It would allow you to change buffers without resetting the format. Thus you would bind a single VAO, then just substitute in different buffers as needed with glBindVertexBuffer. This is also available in ES 3.1.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...