GLESGAE:Making another Mesh with Vertex Buffers
Contents |
GLESGAE - Making another Mesh - Vertex Buffer Objects
Introduction
We've been using Vertex Arrays up till now as they're nice and quick to get up and going with.
However, they have a penalty in that their data is still stored client side, and must be sent to the OpenGL server to be processed before drawing. This is where Vertex Buffer Objects come into play as they've already been processed and sit server-side, with only an identifier being kept in the client.
We will therefore convert our current Vertex Array implementation into a Vertex Buffer Object - VBO - implementation to gain some speed.
Fast Track
We're now on to SVN revision 8... svn co -r 8 http://svn3.xp-dev.com/svn/glesgae/trunk/ glesgae
Creating the Buffers
The creation of VBOs is really just a logical step onwards from what we already have. We need data to put into a VBO in the first place, and we already have this within our two Buffer classes, so wrapping it within a VBO becomes immensely easy.
Both classes we need to add a new variable to store the VBO Id:
unsigned int mVboId;
Along with an accessor:
unsigned int getVboId() const { return mVboId; }
And that's pretty much it apart from the constructors.
Vertex Buffer VBO constructors
Our Vertex Buffer class contains two main constructors - one for when we know the format, and another for generating it on the fly.
Both of these must be edited to intialise our mVboId to 0, and then have the following code in the function body:
glGenBuffers(1U, &mVboId); glBindBuffer(GL_ARRAY_BUFFER, mVboId); glBufferData(GL_ARRAY_BUFFER, mSize, mData, GL_STATIC_DRAW);
And that's it. Our Vertex Buffer automatically generates a VBO for us.
Index Buffer VBO constructor
This is much the same, apart from being an ELEMENT buffer, so the code changes to:
glGenBuffers(1U, &mVboId); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mVboId); glBufferData(GL_ELEMENT_ARRAY_BUFFER, mSize, mData, GL_STATIC_DRAW);
Nice and easy!
Rendering VBOs
We're lucky in that our current Vertex Array method is pretty close to how a VBO is actually used.. we therefore only need to do very small changes to our Fixed Function and Shader Based contexts.
We need to bind the correct VBO before we start sending over any vertex or index information, and this is done by the glBindBuffer( ... ); call again.. so for the Vertex Buffer it would be:
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer->getVboId());
Just before the giant Format switch in both Contexts, and just before the Index Format switch, the Index Buffer would be bound like so:
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer->getVboId());
This just leaves tidying up how we send the information over.
VBOs are already sent over and live server-side in the GL context, therefore any reference to the data client side is null and void.
Therefore, where we may have something like:
glColorPointer(4, GL_UNSIGNED_BYTE, 0, vertexBuffer->getData() + itr->getOffset());
In the Fixed Function Context, we need to change this to:
glColorPointer(4, GL_UNSIGNED_BYTE, 0, reinterpret_cast<char*>(itr->getOffset()));
As the data is already sent over, and the final parameter expects a data pointer format ( which for us, is a char pointer. )
The final draw command which deals with the indices is changed in much the same manner.
Where we currently may have:
glDrawElements(GL_TRIANGLES, indexBuffer->getSize(), GL_UNSIGNED_SHORT, indexBuffer->getData());
It needs to be:
glDrawElements(GL_TRIANGLES, indexBuffer->getSize(), GL_UNSIGNED_SHORT, 0);
As again, the buffer is already sitting in the GL Server ready to be used - we don't need to send anything else over.
And that's it.
We now render using VBOs.
Whoa, hold up! What was this GL_STATIC_DRAW thing?
When we were creating our buffers, we specified GL_STATIC_DRAW in the last parameter of glBufferData. This is to indicate to GL that this is a static buffer - we won't be changing this - but we'll be drawing it many many times.
You also have GL_DYNAMIC_DRAW for when you are going to be changing the contents of the buffer... but take note that as soon as you call glBufferData with a data pointer, that data is copied to the server as it is at that instance.. so if you mess with the data in your program and don't update it with OpenGL, it'll draw whatever you originally set it to! So be careful!
There are a few other parameters you can specify instead, but I suggest you read the Red Book for more information on them.
Building the Example
There is no example this time, as it's just changing how we store and render our Vertex and Index Buffers, so it's all internal code.
Next Time
We take a look at how to manage the mass of objects we now have, and the importance of getting it right.