VBO help

Started by
9 comments, last by Danny02 13 years, 1 month ago
Hi!
I've been trying to understand VBO's and I actually thought that I did, but it seems I still have questions.

I'm loading an OBJ-file to vectors and then use that information to load up buffers of different kinds (normals, texcoords ...).
At the moment I've restricted myself to just vertex positions and the indices of these.
Please let me know if I understand this correctly:
glGenBuffers(1, &vboID[0]); // generates a buffer in slot 0
glBindBuffer(GL_ARRAY_BUFFER, vboID[0]); // binds the buffer as an ARRAY_BUFFER
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*vertices.size(), NULL, GL_STATIC_DRAW); // describes what I want to do with the buffer and the size of it
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*vertices.size(), &vertices[0]); // loads the buffer with data from the vertices vector with starting position 0 in the array


After I've done this, I do the same with the indices but at slot 0:
glGenBuffers(1, &vboID[1]); // generates a buffer in slot 1
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboID[1]); // binds the buffer as an ELEMENT_ARRAY_BUFFER
glBufferData(GL_ELEMENT_ARRAY_BUFFER, ((int)(vFaces.size()) * sizeof(float)), &(vFaces.at(0)), GL_STATIC_DRAW); // loads the buffer with data from the vFaces vector with starting position 0 in the array as well as setting the size of the buffer


After that, I try to draw the model with a "glDrawElements" call (after creating an "earth" object):
glDrawElements(GL_TRIANGLES, sizeof(earth.vFaces), GL_UNSIGNED_INT, &earth.vFaces.at(0));

This gives me absolutely nothing.
When I try to activate and use a VAO then the screen flickers, the graphics driver resets and the program crashes.

What is it that I'm not getting?
The questions that I'm thinking of is how do the program know which VBO to use as a reference for the index VBO? And how does it know the difference between a VBO containing vertex positions and one containing, for example normals?

Thanks for taking the time!
Marcus
Advertisement
glDrawElements(GL_TRIANGLES, sizeof(earth.vFaces), GL_UNSIGNED_INT, &earth.vFaces.at(0));

It should (I think) be earth.vFaces *3 as you are telling OpenGL to render 4 or 2 (depends if earth.vFaces is a int or a short int) vertices. Also the count parameter is in vertices, not in triangles.

Then, if you are using vbo's you don't need to pass &earth.vFaces.at(0). Passing NULL is is sufficient

The proper call should (again, I think) be: glDrawElements(GL_TRIANGLES, earth.vFaces *3, GL_UNSIGNED_INT, NULL);







Hope this helps,

assainator
"What? It disintegrated. By definition, it cannot be fixed." - Gru - Dispicable me

"Dude, the world is only limited by your imagination" - Me


Then, if you are using vbo's you don't need to pass &earth.vFaces.at(0). Passing NULL is is sufficient
[/quote]

It's better than sufficient, if you pass in an arbitrary memory location while your vbo is bound, its going to do the pointer arithmetic:

start of vbo + address of earth.vFaces = garbage in the middle of nowhere

which will probably cause your program to crash. Definitely want (void*)0 here as the last argument, or NULL.


It should (I think) be earth.vFaces *3
[/quote]
I think should be "earth.vFaces.size()*3", earth.vFaces looks like a vector, thus doesn't make any sense to multiply a std::vector by 3.


The questions that I'm thinking of is how do the program know which VBO to use as a reference for the index VBO? And how does it know the difference between a VBO containing vertex positions and one containing, for example normals?
[/quote]

The index buffer is always and only the thing bound to GL_ELEMENT_ARRAY_BUFFER. When you start drawing, it pulls indices from this buffer. Vertex and normal data are a little different, they work through the concept of ArrayPointers, which I conspicuously don't see in your code (a problem if you don't have it).

When you draw it doesn't matter if a buffer or not is bound to GL_ARRAY_BUFFER, it only reads data from where the pointers are set. So to draw a an array of vertices:

glBindBuffer(GL_ARRAY_BUFFER, id);
glVertexPointer(...);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexId);
glEnableClientState(GL_VERTEX_ARRAY);
glDrawElements();
glDisableClientState(GL_VERTEX_ARRAY);


If you want to draw normals, you do the same thing with glNormalPointer, theres a whole family of gl*Pointer functions. However if you're using shaders and not the fixed pipeline, this changes a little bit to:

glBindBuffer(GL_ARRAY_BUFFER, id);
glVertexAttribPointer(...);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(attribId);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexId);
glDrawElements();
glDisableVertexAttribArray(attribId);


Look up the manual page for any of the new functions I've defined here if you want to know how to use them.
[size=2]My Projects:
[size=2]Portfolio Map for Android - Free Visual Portfolio Tracker
[size=2]Electron Flux for Android - Free Puzzle/Logic Game


Then, if you are using vbo's you don't need to pass &earth.vFaces.at(0). Passing NULL is is sufficient


It's better than sufficient, if you pass in an arbitrary memory location while your vbo is bound, its going to do the pointer arithmetic:

start of vbo + address of earth.vFaces = garbage in the middle of nowhere

which will probably cause your program to crash. Definitely want (void*)0 here as the last argument, or NULL.


It should (I think) be earth.vFaces *3
[/quote]
I think should be "earth.vFaces.size()*3", earth.vFaces looks like a vector, thus doesn't make any sense to multiply a std::vector by 3.


The questions that I'm thinking of is how do the program know which VBO to use as a reference for the index VBO? And how does it know the difference between a VBO containing vertex positions and one containing, for example normals?
[/quote]

The index buffer is always and only the thing bound to GL_ELEMENT_ARRAY_BUFFER. When you start drawing, it pulls indices from this buffer. Vertex and normal data are a little different, they work through the concept of ArrayPointers, which I conspicuously don't see in your code (a problem if you don't have it).

When you draw it doesn't matter if a buffer or not is bound to GL_ARRAY_BUFFER, it only reads data from where the pointers are set. So to draw a an array of vertices:

glBindBuffer(GL_ARRAY_BUFFER, id);
glVertexPointer(...);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexId);
glEnableClientState(GL_VERTEX_ARRAY);
glDrawElements();
glDisableClientState(GL_VERTEX_ARRAY);


If you want to draw normals, you do the same thing with glNormalPointer, theres a whole family of gl*Pointer functions. However if you're using shaders and not the fixed pipeline, this changes a little bit to:

glBindBuffer(GL_ARRAY_BUFFER, id);
glVertexAttribPointer(...);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(attribId);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexId);
glDrawElements();
glDisableVertexAttribArray(attribId);


Look up the manual page for any of the new functions I've defined here if you want to know how to use them.
[/quote]

Thanks for the answers. It did the trick. Figures it was me not thinking straight about the count parameter and the NULL pointer at the end.
I'm not using any deprecated functions (OGL 3.x - 4) so the gl*Pointer functions won't cut it.
The latter code block is closer to what I'm doing.

I'm now trying to get the program to take my normals into account when displaying the model.

I'm sorry, but I just can't seem to wrap my head around how this works.
This is the code where I enable all my buffers and my VAO. Note that the normals and texture coordinate buffers are commented out, since they won't work. Obviously :)
void CModel::initVBO() {
glGenVertexArrays(1, &vaoID[0]); // generates a vertex array to slot 0 in my VAO array
glBindVertexArray(vaoID[0]); // binds the vertex array in slot 0

// vertex buffer
glGenBuffers(1, &vboID[0]); // generates a VBO in slot 0 in my VBO array
glBindBuffer(GL_ARRAY_BUFFER, vboID[0]); // binds the buffer as an ARRAY_BUFFER in slot 0
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*vertices.size(), NULL, GL_STATIC_DRAW); // describes what I want to do with the buffer + size
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*vertices.size(), &vertices[0]); // fills the buffer with data

/*
// texture buffer
glGenBuffers(1, &vboID[1]);
glBindBuffer(GL_ARRAY_BUFFER, vboID[1]);
glBufferData(GL_ARRAY_BUFFER, ((int)(texcoords.size()) * 3 * sizeof(float)), &(texcoords.at(0)), GL_STATIC_DRAW);

// normal buffer
glGenBuffers(1, &vboID[2]);
glBindBuffer(GL_ARRAY_BUFFER, vboID[2]);
glBufferData(GL_ARRAY_BUFFER, ((int)(normals.size()) * 3 * sizeof(float)), &(normals.at(0)), GL_STATIC_DRAW);
*/

// index buffer
glGenBuffers(1, &vboID[1]); // generates a buffer in slot 1
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboID[1]); // binds the buffer as an ELEMENT_ARRAY_BUFFER (since this concerns indices)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, ((int)(vFaces.size()) * sizeof(float)), &(vFaces.at(0)), GL_STATIC_DRAW); // loads the buffer with data, sets the size of the vector vFaces times float and tells it to start at position 0 in the vector vFaces

glVertexAttribPointer((GLuint)0, 3, GL_FLOAT, GL_FALSE, 0, 0); // this points to the 0 slot vertex array object

glEnableVertexAttribArray(0); // this enables the vertex array in slot 0
// glBindVertexArray(0); // this inactivates my vertex array... from a tutorial... why would I want to do this?
}


As you can see, some question marks in the comments. Am I going about this correctly? I've tried generating and binding another vaoID to the VBO connected to my normals. But that didn't work... it sort of took the normals as vertex coordinates and that made my model "spiky" and completely round... which isn't right, since the model I'm loading at the moment is a human model.
I'm not understanding this.

Thanks for your time, guys. It's appreciated. It took some time to "sort of" understand VBO's. VAO's are proving to be a bit more difficult than I thought.

glDrawElements(GL_TRIANGLES, sizeof(earth.vFaces), GL_UNSIGNED_INT, &earth.vFaces.at(0));

It should (I think) be earth.vFaces *3 as you are telling OpenGL to render 4 or 2 (depends if earth.vFaces is a int or a short int) vertices. Also the count parameter is in vertices, not in triangles.

Then, if you are using vbo's you don't need to pass &earth.vFaces.at(0). Passing NULL is is sufficient

The proper call should (again, I think) be: glDrawElements(GL_TRIANGLES, earth.vFaces *3, GL_UNSIGNED_INT, NULL);







Hope this helps,

assainator


Thanks, assainator. It helped a lot!
You're throwing around the term "slot 0" in a lot of places, I don't think you really understand what it means.

Particularly here:

glVertexAttribPointer((GLuint)0, 3, GL_FLOAT, GL_FALSE, 0, 0); // this points to the 0 slot vertex array object

I think you should read up on this function which you will need:
glGetAttribLocation

Basically, lets consider a hypothetical variable in your shader:

in vec3 myNormal;

When you compile the shader, an attribute location is assigned to this variable, maybe it will be "3". Another variable like "myPosition" maybe gets assigned to "1". For a particular variable, you query the location with glGetAttribLocation, then use this as an argument to the glVertexAttribPointer/glEnableVertexAttribArray.

Also you don't really need VAO for anything, they're just a thin wrapper around several VBOs. They maybe save you one or two calls when setting up to render, but that's about it. If you're having trouble you can just stick with vertex buffers until you work out all the issues.
[size=2]My Projects:
[size=2]Portfolio Map for Android - Free Visual Portfolio Tracker
[size=2]Electron Flux for Android - Free Puzzle/Logic Game


It should (I think) be earth.vFaces *3

I think should be "earth.vFaces.size()*3", earth.vFaces looks like a vector, thus doesn't make any sense to multiply a std::vector by 3.
[/quote]
I wouldn't know, as I haven't seen all of his code. I interpreted earth.vFaces as a face-count value instead of a vector.




assainator


"What? It disintegrated. By definition, it cannot be fixed." - Gru - Dispicable me

"Dude, the world is only limited by your imagination" - Me

Could one explain the VAO - VBO relationship like this?
When having many polygons on screen, VBO's are used to get a performance boost. When having a lot of VBO's (many, many objects) on screen, VAO's are used to get a performance boost.

Or am I wrong about the uses?
If not, then VBO's are practically required with most modern games (many polygons per object), while the addition of VAO's is more of a choice based on how many objects that would be on screen at the same time. An example for when to use VBO's without VAO's would then be when programming a game like DOOM 3, where there's not a lot of models on screen at the same time but the objects that are on screen are using many polygons. An example of a game that would benefit from VAO's would be Empire: Total War and Minecraft. Empire because of the massive amounts of units on screen at the same time and Minecraft because of all the blocks (which only have 6 quads (12 tris) but are in massive amounts).

Again, am I understanding the use of VAO and VBO now? I seem to have problems understanding the manual, it's easier when I can discuss this with you guys.

Thanks again.
I'd say you've got the basic idea. Don't overstate the performance benefit of VAOs though, I'd imagine it to be fairly trivial. You're basically just replacing 1-10 or so opengl calls with one call, which could help a bit, but probably is not a huge deal (it may not actually be any different under the hood, if the driver is just replacing the vao call with the equivalent pointer calls). I've read a few threads on opengl.org and from what I can tell nobody actually knows if they give any performance benefit or not (it's up to the driver implementers to make it so, which seems has really not been done).

Please also don't assume that every 'thing' on the screen needs its own VBO. You mention total war, and even if there might be 500 infantry units on the battlefield, this is likely just a single VBO rendered 500 times.

The number of VBOs you have will boil down to personal preference, number of passes, how you organize your models, etc. VAO are optional if you want to use them, the code may be a little bit cleaner, but it's up to you. Don't lose any sleep over them :)
[size=2]My Projects:
[size=2]Portfolio Map for Android - Free Visual Portfolio Tracker
[size=2]Electron Flux for Android - Free Puzzle/Logic Game

I'd say you've got the basic idea. Don't overstate the performance benefit of VAOs though, I'd imagine it to be fairly trivial. You're basically just replacing 1-10 or so opengl calls with one call, which could help a bit, but probably is not a huge deal (it may not actually be any different under the hood, if the driver is just replacing the vao call with the equivalent pointer calls). I've read a few threads on opengl.org and from what I can tell nobody actually knows if they give any performance benefit or not (it's up to the driver implementers to make it so, which seems has really not been done).

Please also don't assume that every 'thing' on the screen needs its own VBO. You mention total war, and even if there might be 500 infantry units on the battlefield, this is likely just a single VBO rendered 500 times.

The number of VBOs you have will boil down to personal preference, number of passes, how you organize your models, etc. VAO are optional if you want to use them, the code may be a little bit cleaner, but it's up to you. Don't lose any sleep over them :)


Haha, no I won't lose any sleep at all (have skipped them).
Now I need to figure out how to interleave (or assign) normals and texture coordinates with my vertex positions. Any tips? I was thinking of binding one VBO for each value... but that feels like defeating the purpose of using VBO's. And then I noticed that you can use an offset with the drawElements call.
Do I interleave the values in a new vector containing: [0]: X, Y, Z (3 values for vertex position) | [1]: X, Y, Z (3 values for normals) | [2]: U, V (2 values for texture coordinates (UV)) and then start over at position [3] with the next positions?
Having done this, how the hell do I get the glDrawElements call to actually draw all three?

This topic is closed to new replies.

Advertisement