PDA

View Full Version : VAO/VBO for meshes



Avithohol
08-15-2011, 03:50 AM
Hello All,

iam reading OpenGL Superbible 5th and dealing with VAO/VBO part. I can render my meshes fairly easy and efficiently by them.

What i do not see still, how should i deal with massive number of meshes, lets say 9999.

Please note, that this sample code is for demonstration only:

1. Should i have 9999 VAO generated for each mesh, and upload data of a single mesh only then repeat it 9998 times ?


//1 VAO is a single mesh which has 2 VBO
glGenVertexArrays(1, &mesh0_VAO);
glGenBuffers(1, &mesh0_verts);
glGenBuffers(1, &mesh0_indicies);

glBindVertexArray(mesh0_VAO);

// VBO 1
glBindBuffer(GL_ARRAY_BUFFER, mesh0_verts);
glBufferData(GL_ARRAY_BUFFER, ...., GL_STATIC_DRAW);

// VBO 2
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh0_indicies);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, .... GL_STATIC_DRAW);


2. Or should i just create only one VAO for all the meshes and have 9999*2 VBO-s for different different meshes.


//1 VAO for all the meshes which has 9999*2 VBO
glGenVertexArrays(1, &allmeshes);
glGenBuffers(1, &mesh0_verts);
glGenBuffers(1, &mesh0_indicies);
glGenBuffers(1, &mesh1_verts);
glGenBuffers(1, &mesh1_indicies);
...
glGenBuffers(1, &mesh9999_verts);
glGenBuffers(1, &mesh9999_indicies);

glBindVertexArray(allmeshes);

// mesh 0 VBO-s
glBindBuffer(GL_ARRAY_BUFFER, mesh0_verts);
glBufferData(GL_ARRAY_BUFFER, ...., GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh0_indicies);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, .... GL_STATIC_DRAW);
// mesh 1 VBO-s
glBindBuffer(GL_ARRAY_BUFFER, mesh1_verts);
glBufferData(GL_ARRAY_BUFFER, ...., GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh1_indicies);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, .... GL_STATIC_DRAW);
...
// mesh 9999 VBO-s
glBindBuffer(GL_ARRAY_BUFFER, mesh9999_verts);
glBufferData(GL_ARRAY_BUFFER, ...., GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh9999_indicies);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, .... GL_STATIC_DRAW);


For a simple example a single mesh is:
-It has lists for its vertex positions, texturecoords and indicies
-It has single texture assigned

Mesh could be anything a rock, a bush, a wall, anything.

What do you think is it 1. or 2. perform better,
iam only interested in performance :)


Thank You
A

AleaIactaEst
08-15-2011, 05:35 AM
Hi im just like you a newbie to OpenGL and reading OpenGL SuperBible 5th edition :)

I guess the second will be much faster than the first, since you only need two OpenGL calls in your render function to send all the objects to the pipeline.

glBindVertexArray(allmeshes);
glDrawElements([...]);
But one question that comes into my mind when drawing objects that way, how are you going to change the positions of the objects? Because that way all the Objects will get drawn with their origins at the same position. There is no way to change uniform variables in between.
Of course you could hardcode the positions in your vertex data before storing the data in your buffers, but thats only a solution for static objects, that wont change their position/orientation for the lifetime of your application (you could still change the data in the buffers of course, but that would result in bad performance i think)

So i'd use the first approach for objects that you want to be able to modifiy (i.e. translate, rotate, scale) and the second one for 'static' objects.

Avithohol
08-15-2011, 11:49 PM
Hi im just like you a newbie to OpenGL and reading OpenGL SuperBible 5th edition :)

I guess the second will be much faster than the first, since you only need two OpenGL calls in your render function to send all the objects to the pipeline.

glBindVertexArray(allmeshes);
glDrawElements([...]);
But one question that comes into my mind when drawing objects that way, how are you going to change the positions of the objects? Because that way all the Objects will get drawn with their origins at the same position. There is no way to change uniform variables in between.
Of course you could hardcode the positions in your vertex data before storing the data in your buffers, but thats only a solution for static objects, that wont change their position/orientation for the lifetime of your application (you could still change the data in the buffers of course, but that would result in bad performance i think)

So i'd use the first approach for objects that you want to be able to modifiy (i.e. translate, rotate, scale) and the second one for 'static' objects.


Hello AleaIactaEst,

very good question :)
If both of the solutions are "allowed" then i have to walk down the path to split my world to "static" and "dynamic" objects.

I successfully implemented the second solution (single VAO and 9999 VBO),
but the first solution gave me headache:
Iam not sure where to bind and unbind the VAO/VBO for each separated meshes.


Let me divide my code to Initialization phase and Rendering phase, hope you dont get lost in the details :) Please focus only the (Bind) and (Unbind) sections.

This works perfectly if i dont use VAO-s (just manually the VBO-s) but as soon as i introduce VAO-s and try to enable and "switch" between them, i only see the lastly drawn mesh (pyramid)


Unfortunately even the Opengl Superbible 5th samples dont involve heavy switch between VAO-s, they usually create only one VAO :(




// INITIALIZATION PHASE

//Cube

GLfloat cube_points[] = { 0.5f, -0.5f, .... 1.0f }; // vertex positions (4 floats per vertex)
GLfloat cube_custom[] = { 0,0,0,0,......,0}; // some values for each vertex
GLuint cube_indexes[] = { 0, 2, 1,.... 22, 21 }; // vertex indicies

// 1 VAO for cube
glGenVertexArrays(1, &vao_cube);
glBindVertexArray(vao_cube);

// 3 VBO
glGenBuffers(1, &vbo_cube_verts);
glGenBuffers(1, &vbo_cube_custom);
glGenBuffers(1, &vbo_cube_indexes);

glBindBuffer(GL_ARRAY_BUFFER, vbo_cube_verts);
glBufferData(GL_ARRAY_BUFFER, ..., cube_points, GL_STATIC_DRAW);
glEnableVertexAttribArray(VERTEX_ATTRIB_0);
glVertexAttribPointer(VERTEX_ATTRIB_0, 4, GL_FLOAT, GL_FALSE, 0, 0);

glBindBuffer(GL_ARRAY_BUFFER, vbo_cube_custom);
glBufferData(GL_ARRAY_BUFFER, ..., cube_custom, GL_STATIC_DRAW);
glEnableVertexAttribArray(VERTEX_ATTRIB_1);
glVertexAttribPointer(VERTEX_ATTRIB_1, 4, GL_FLOAT, GL_FALSE, 0, 0);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_cube_indexes);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, ..., cube_indexes, GL_STATIC_DRAW);

glBindVertexArray(0);


//Pyramid

GLfloat pyra_points[] = { ... }; // vertex positions (4 floats per vertex)
GLuint pyra_indexes[] = { 2,1,0, .... 3,0,4 }; // vertex indicies

// 1 VAO for pyramid
glGenVertexArrays(1, &vao_pyra);
glBindVertexArray(vao_pyra);

// 2 VBO
glGenBuffers(1, &vbo_pyra_verts);
glGenBuffers(1, &vbo_pyra_indexes);

glBindBuffer(GL_ARRAY_BUFFER, vbo_pyra_verts);
glBufferData(GL_ARRAY_BUFFER, ..., pyra_points, GL_STATIC_DRAW);
glEnableVertexAttribArray(VERTEX_ATTRIB_0);
glVertexAttribPointer(VERTEX_ATTRIB_0, 4, GL_FLOAT, GL_FALSE, 0, 0);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_pyra_indexes);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, ..., pyra_indexes, GL_STATIC_DRAW);

glBindVertexArray(0);




// RENDERING PHASE
{

// ...

Enable Shader 1

glBindVertexArray(vao_cube);
glDrawElements(...);
glBindVertexArray(0);

Disable Shader 1


Enable Shader 2

glBindVertexArray(vao_pyra);
glDrawElements(...);
glBindVertexArray(0);

Disable Shader 2

// ...
}

AleaIactaEst
08-16-2011, 02:19 AM
Just yesterday i had exactly the same problem like you have now, but the solution is pretty easy:

Before calling glDrawElements([...]) you need to bind the GL_ELEMENT_ARRAY_BUFFER that holds the indices you need to draw the objects, otherwise OpenGL does not 'find' them, because the buffer bindings of the GL_ELEMENT_ARRAY_BUFFER are not(!) stored in the vao.

After reading the whole chapter about vbo's/vao's again i figured out that only the GL_ARRAY_BUFFER bindings will be stored in the vao at the moment you're calling glVertexAttribPointer([...]), but since there is no such function for the GL_ELEMENT_ARRAY_BUFFER binding, it won't be stored in the vao.

SuperBible pages for reference:
P.489: "Storing Vertex Indices in Buffers" 1st - 3rd paragraph
P.491: "Using Vertex Array Objects to Organize Your Buffers" last paragraph on this page

In my opinion an important topic like geometry managment is discussed too late and on too few pages, which makes it quite difficult to understand things like that...

Alfonse Reinheart
08-16-2011, 02:23 AM
because the buffer bindings of the GL_ELEMENT_ARRAY_BUFFER are not(!) stored in the vao.

That is absolutely not true. The specification is quite clear on this: GL_ELEMENT_ARRAY_BUFFER is part of the VAO.

If you're seeing something different, then your implementation has a bug.

AleaIactaEst
08-16-2011, 02:31 AM
So you're saying i could just bind a buffer to the GL_ELEMENT_ARRAY_BUFFER binding and that binding will then be stored in the vertex array object, so that i only need to call
glBindVertexArray()
glDrawElements()?

But that doesnt work. If i bind the buffer containing the indices to the GL_ELEMENT_ARRAY_BUFFER binding before calling glDrawElements() it does work. ~

Avithohol
08-16-2011, 03:03 AM
This is exactly how it should work, but it does not for me :(



glBindVertexArray() //Bind
glDrawElements() //Draw
glBindVertexArray(0) //Unbind


Will try what AleaIactaEst said, but anyway i would like to do it according to the "big book".

Alfonse Reinheart
08-16-2011, 03:16 AM
But that doesnt work.

It does work. I do it [url=http://www.arcsynthesis.org/gltut/]all the time in my tutorials[/i]. This has been tested on both AMD and NVIDIA hardware.

If it's not working for you, then either you have some bad drivers or you're doing it wrong. Are you sure that you are binding the index buffer while the VAO is bound?

AleaIactaEst
08-16-2011, 03:23 AM
Yeah im binding the index buffer while the vao is bound, im doing it in exactly the same way as Avithohol.
But unless the index buffer is bound when calling glDrawElements, nothing gets drawn.

Maybe storing GL_ELEMENT_ARRAY_BUFFER bindings in vaos isn't supported in OpenGL version 2.1? Im using mesa 7.7.1

Avithohol
08-16-2011, 03:50 AM
I beginning to think that it is indeed driver issue:

I have Win7 and ATI x1600 with newest drivers for this old card, which knows only gl v2.1 max, and my intention is to write library which is uses 3.x core profile but so simple that can run on gl2.1 hardwares.

With only VBO-s it was working fine. Maybe this is the point where i failed :) and found a feature which i cannot avoid neither implement in my library.



I think my library will support VBO only rendering then.

AleaIactaEst
08-16-2011, 05:56 AM
I think my library will support VBO only rendering then.

But you said when using vaos one object does still get rendered, so that can't be entirely true. Or did it only get rendered because the last buffers were still bound?

Did you try my approach of binding the GL_ELEMENT_ARRAY_BUFFER before calling glDrawElements()?

Avithohol
08-16-2011, 06:38 AM
Yes AleaIactaEst, i think its working because the last buffer is still bound.

Could not test it out entirely yet, i can do it at home only, two days to go :(

Avithohol
08-16-2011, 11:44 PM
Okay, i have tested it now !
It seems with my gl2.1 driver, no state is bound to VAO.
I have to bind all the VBO-s, then all the objects are drawn correctly. So its like the VAO is not even there.

It seems that AleaIactaEst has to bind only GL_ELEMENT_ARRAY_BUFFER with the mesa driver.

Anyway I carry on with my "run basic 3.x core profile code on 2.1 hardware" library and see what surprises are about to come !

Thank You all the time and energy to read the thread !
Happy coding !

BionicBytes
08-17-2011, 01:44 AM
see what surprises are about to come
I'm curious. Did you check for the support of the extension (GL_ARB_VERTEX_ARRAY_OBJECT)?
What was the result?