4. OpenGL 4 Vertex Array Objects (VAO)
Introduction
With the removal of OpenGL states and the fixed function mode, we no longer have any glEnable calls, which means no glEnable(GL_TRIANGLES), or similar calls. It also means no more calls to glVertex or glColor. What this means is we need a new way of transferring data to the graphics card to render our geometry. We could use VBO’s which I introduced in one of the terrain tutorials, or we could use a new feature in OpenGL 3+, known as VAO’s. VAO’s are Vertex Array Objects and allow you to store multiple VBO’s in one VAO. With this ability, we can now store vertex data and colour data in separate VBO’s, but in the same VAO. The same goes for all types of data you usually send through as a VBO, including data for normals or anything that you need on a per-vertex rate.
What a VAO is, is a way to store object information straight on the graphics card, instead of sending vertices through to the graphics card as we need them. This is how Direct3D works as it doesn’t have the immediate mode which OpenGL had up until OpenGL 3.x. This then means our application doesn’t have to transfer data to and from the graphics card and we get extreme increases in performance.
The best part, is that this isn’t too difficult to set up. It just means that instead of a bunch of glVertex calls, we store all our vertices in an array and then place that into a VBO and then into a VAO. OpenGL takes care of everything behind the scenes, and we get all the advantages that come with VBO’s and VAO’s.
Coding
While I understand you may not have done the terrain tutorial which introduces VBO’s, I’m going to be starting from the basics and I will still be explaining everything as I go. The best part is we don’t have to do too much to get the output for this tutorial, which is a square on the screen.
Open up opengl_3.h and inside of it we are going to create two variables and one method. The method will be take no parameters and will return nothing, but inside of it we will be creating our VAO and VBO to draw a square. In that mindset, I am going to call the method createSquare.
void createSquare(void); // Method for creating our squares Vertex Array Object
Our two variables are going to both be arrays of unsigned int’s and they are both going to be of length 1, so we have one VAO and one VBO. I am calling these vaoID and vboID and am using these as private variables in our class.
After removing the OpenGL states and fixed function mode, we no longer have glEnable calls, which means no glEnable (GL_TRIANGLES) calls or similar calls. However, this does not mean that generic viagra drug sales have disappeared. You can read more about this medicine on this website.
unsigned int vaoID[1]; // Our Vertex Array Object unsigned int vboID[1]; // Our Vertex Buffer Object
Now that’s not so scary, let’s go and jump into opengl_3.cpp and start making use of this. Inside of the setupScene method I am adding the call to createSquare and next we will start creating this method. Keep in mind that I am creating the square and the VAO and VBO before I actually use them, otherwise we might run into issues with empty memory.
void OpenGLContext::setupScene(void) { ... createSquare(); // Create our square }
And now to create our createSquare method. I have jumped down to the bottom of the opengl_3.cpp file, however you can place this anywhere in the file if you prefer to order your methods by name or something of the like. Here is what the empty method will initially look like:
/** createSquare is used to create the Vertex Array Object which will hold our square. We will be hard coding in the vertices for the square, which will be done in this method. */ void OpenGLContext::createSquare(void) { }
Before we create our VAO and VBO, we need some data which describes the vertices for our shape. I am drawing a square made up of two triangles, so we need 6 vertices, 3 for each triangle. Then, because we have 6 vertices, each vertex is made up of 3 values, the x, y and z coordinates, giving us a total of 18 values we need to describe our square. These are going to be float values and stored in an array called vertices. As I am using a dynamic array, don’t forget to call the delete method afterwards to make sure we free up our memory again.
/** createSquare is used to create the Vertex Array Object which will hold our square. We will be hard coding in the vertices for the square, which will be done in this method. */ void OpenGLContext::createSquare(void) { float* vertices = new float[18]; // Vertices for our square delete [] vertices; // Delete our vertices from memory }
Now it’s time to fill in some values for the vertices. I am going to place all the vertices at 0 on the z axis and I am going to give the square a size of 1 unit. That means I need to go up and to the left half a unit, and down and to the right half a unit as 0.5 + 0.5 = 1, giving us a length of 1 for the sides of the square. So the top left vertex is (-0.5, 0.5, 0.0) and the bottom right vertex is (0.5, -0.5, 0.0). Filling in all 18 values based on this, we get:
/** createSquare is used to create the Vertex Array Object which will hold our square. We will be hard coding in the vertices for the square, which will be done in this method. */ void OpenGLContext::createSquare(void) { float* vertices = new float[18]; // Vertices for our square vertices[0] = -0.5; vertices[1] = -0.5; vertices[2] = 0.0; // Bottom left corner vertices[3] = -0.5; vertices[4] = 0.5; vertices[5] = 0.0; // Top left corner vertices[6] = 0.5; vertices[7] = 0.5; vertices[8] = 0.0; // Top Right corner vertices[9] = 0.5; vertices[10] = -0.5; vertices[11] = 0.0; // Bottom right corner vertices[12] = -0.5; vertices[13] = -0.5; vertices[14] = 0.0; // Bottom left corner vertices[15] = 0.5; vertices[16] = 0.5; vertices[17] = 0.0; // Top Right corner delete [] vertices; // Delete our vertices from memory }
So we’ve now filled in our vertices coordinates, we need to go and create a Vertex Array Object which is done with a call to glGenVertexArrays. We then need a call to glBindVertexArray to make the VAO active. Once the VAO is active, we need to call glGenBuffers to create our Vertex Buffer Object, and then we also need to bind it with glBindBuffer.
So it goes in this order:
1. Generate Vertex Array Object
2. Bind Vertex Array Object
3. Generate Vertex Buffer Object
4. Bind Vertex Buffer Object
Once we have done step 4, we need to fill our VBO with the vertex data. When creating VBO’s, you can set why type of VBO it is, and in this case we are going for GL_STATIC_DRAW, this tells OpenGL that we do not intend on changing the data in any way, and we just want to be able to render it. There are types for data which changes as well, for all types, I suggest checking out the OpenGL API or the OpenGL wiki: http://www.opengl.org/wiki/Vertex_Buffer_Object
Now, we need to call glBufferData to set the data for our VBO to the float array we created above, before telling OpenGL that this VBO is for storing vertex data. Finally we clean up by disabling our Vertex Attribute Array and also disabling our Vertex Array Object.
/** createSquare is used to create the Vertex Array Object which will hold our square. We will be hard coding in the vertices for the square, which will be done in this method. */ void OpenGLContext::createSquare(void) { ... vertices[15] = 0.5; vertices[16] = 0.5; vertices[17] = 0.0; // Top Right corner glGenVertexArrays(1, &vaoID[0]); // Create our Vertex Array Object glBindVertexArray(vaoID[0]); // Bind our Vertex Array Object so we can use it glGenBuffers(1, vboID); // Generate our Vertex Buffer Object glBindBuffer(GL_ARRAY_BUFFER, vboID[0]); // Bind our Vertex Buffer Object glBufferData(GL_ARRAY_BUFFER, 18 * sizeof(GLfloat), vertices, GL_STATIC_DRAW); // Set the size and data of our VBO and set it to STATIC_DRAW glVertexAttribPointer((GLuint)0, 3, GL_FLOAT, GL_FALSE, 0, 0); // Set up our vertex attributes pointer glEnableVertexAttribArray(0); // Disable our Vertex Array Object glBindVertexArray(0); // Disable our Vertex Buffer Object delete [] vertices; // Delete our vertices from memory }
What we have now, is a VAO with a VBO storing vertex data for a square. So far it’s been straight forward, so now we need to render this data to the screen. Go ahead and jump to our renderScene method and after we set our glUniformMatrix4vf variables for our shader from the previous tutorial, we need to add three lines of code to render. First off we need to enable our Vertex Array Object, then we need to draw the VAO and finally disable the VAO. Like so:
glBindVertexArray(vaoID[0]); // Bind our Vertex Array Object glDrawArrays(GL_TRIANGLES, 0, 6); // Draw our square glBindVertexArray(0); // Unbind our Vertex Array Object
And well done, you can now create a square using no immediate mode and storing everything on the graphics card, which will run at maximum framerates. One more optimization to this, would be to use a GL_TRIANGLE_STRIP instead of GL_TRIANGLES and you could get away with 4 vertices. In the next tutorial we will look at adding colour to our square.
Downloads
Visual Studio 2010 Project
PDF version
If you have any questions you can comment below or email me at swiftless@gmail.com
First up, thanks for such helpful tutorials! Super clear and easy to follow, even for a beginner like me!
Quick question, I’ve followed along until then end of your coloring tutorial, and everything compiles and runs peachy, I have a nice four color box, but the box is only about 5 pixels in size… Any reason you can think of for this? I downloaded your source, and you have a nice, big, beautiful box on your render. Comparing code back and fourth between my project, and your source, I couldn’t spot any immediate differences… Maybe something to do with my projection matrix? Any help would be loved.
I’ve sorted it out. The newest .zip from GLM did not seem to contain So I found an older version, and it seems to work just fine! Again, thanks for the excellent tutorials.
So i made a function that lets you choose the values for the squares but 0.5f size is huge. anyone know why?
createSquare(-1,1,1,1);
void OpenGLContext::createSquare(float x,float y,float width,float height)
{
float* vertices = new float[18];
vertices[3] = x;vertices[4] = y;//top left
vertices[6] = x + width;vertices[7] = y;//top right
vertices[15] = x + width;vertices[16] = y;
vertices[0] = x;vertices[1] = y-height;//bottom left
vertices[12] = x;vertices[13] = y-height;
vertices[9] = x + width;vertices[10] = y-height; //bottom right
//z axis
vertices[2] = 0;
vertices[5] = 0;
vertices[8] = 0;
vertices[11] = 0;
vertices[14] = 0;
vertices[17] = 0;
//
glGenVertexArrays(1, &voaID[0]);
glBindVertexArray(voaID[0]);
glGenBuffers(1, vboID);
glBindBuffer(GL_ARRAY_BUFFER,vboID[0]);
glBufferData(GL_ARRAY_BUFFER,18 * sizeof(GLfloat), vertices,GL_STATIC_DRAW);
glVertexAttribPointer((GLuint)0,3,GL_FLOAT,GL_FALSE,0,0);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
delete [] vertices;
}
When I run the program it displays a black screen and the console window says that it is using opengl -1.1. It also says error validating shader 0. What could be wrong?
That means you don’t have updated drivers
What would improve this a lot is adding an a href= to this:
“While I understand you may not have done **the terrain tutorial which introduces VBO’s**…”
Simple and problem solved.
Thanks for the good work though.
Thanks for the tutorials, they’re still being used.
I have an interesting problem for you. The glVertexAttribPointer and glBufferData command pair are not behaving as i would expect. If i create an array with ints, and set the attrib pointer field to GL_INT then my data passes through just fine. However if i create an array of floats, and set the attrib pointer to GL_FLOAT i get garbage in my shaders. I’m using VS2010 and my research seems to indicate that the .NET environment cannot support 4 byte floats, only 8 byte. However if i use GL_DOUBLE with either a float or double array of data i get nothing in my shaders.
Thoughts?
glEnable(GL_TRIANGLES) was never a valid call. Maybe you meant glBegin(GL_TRIANGLES)?
Yes sorry, that’s exactly what I meant! Nice catch!
Thanks,
Swiftless
Why not change the “// Disable our Vertex Array Object” comment to the correct meaning? Might prevent future misunderstandings.
This might seem a bit picky, but you outlined the steps as follows:
So it goes in this order:
1. Generate Vertex Array Object
2. Bind Vertex Array Object
3. Generate Vertex Buffer Object
4. Bind Vertex Buffer Object
Steps 2 and 3 can be swapped with no difference to the outcome.
Thanks for the good tutorial!
Thanks for the tutorial. It’s a good introduction to VAOs.
However, I feel that these two lines in the OpenGLContext::createSquare function are misleading:
glEnableVertexAttribArray(0); // Disable our Vertex Array Object
glBindVertexArray(0); // Disable our Vertex Buffer Object
glEnableVertexAttribArray doesn’t disable the vertex array object as stated in your comment. Actually, it enables attribute 0, which was initialized in the previous line by glVertexAttribPointer. This state is saved in the VAO that is currently bound.
glBindVertexArray(0) doesn’t disable the vertex buffer object either, it binds the default VAO.
Hi mysterycoder,
Yes and no, I just consulted the specification for you.
glEnableVertexAttribArray is correct with a value of 0, it does initialise vertex attrib array set prior and that’s a mistake on my part.
glBindVertexArray with a value of 0 however is used to break the current vertex array object binding. There is no such thing as a ‘default’ VAO.
Cheers,
Swiftless
That was a quick response!
Sorry about the incorrect information, I should have checked the specification myself. I think I had read that in a thread on the OpenGL forums.
There seems to be a lot of misinformation surrounding VAOs and it seems I’m adding to it. 🙂
Thanks again for the tutorials,
mysterycoder
A bit late now though I was also confused by those two lines and maybe my comment is helpful for someone.
The usage and parameters of those two calls are correct but indeed comments are not.
glEnableVertexAttribArray(0);
enables the previously specified attribute, so the comment should be
glEnableVertexAttribArray(0); // Enable vertex attribute with ID 0
Second line does not unbind the VBO but the VAO. See above where you write:
glBindVertexArray(vaoID[0]); // Bind our Vertex Array Object so we can use it
Comment should read
glBindVertexArray(0); // Disable our Vertex Array Object
Hi noname,
That’s entirely correct, thanks for picking it up! Binding anything to 0 will disable it.
Thanks,
Swiftless
Hello, first of all, thanks a lot for these great tutorials, thanks for the time you spend in writing these things!
I think these are the best tutorials on the net, heck Ive bought a couple of OpenGL books 50$ a piece and they all end using theyre own libraries.
Heres my question:
I want to create a sprite renderer system.
As far as I understand it, what I need to do is to create a quad and bind it to a texture.
How should approach this? Im thinking of creating a function “CreateQuad” that creates the vertices at the origin then translate it using matrices?
I would like to know if you can also point me to a parametric algorithm so I can create quads of any width and height?
thank you
make a scaling matrix that scales your x and y values then push points for a unit square through the matrix
if you want to translate as well you will have a matrix like below
[ S_x, 0, P_x ]
[ 0, S_y, P_y]
[0, 0, 1]
Where:
S_x is the x scale
S_y is the y scale
P_x is the x position
P_y is the y postion
Hi, swiftless, I previously wrote a comment about the line regarding
glEnableVertexAttribArray(0); // Disable our Vertex Array Object.
But the comment is no longer here, did you delete it?
Hi Mario,
All comments on the site go through a manual approval at first by myself, and if approved, all your future comments will be approved automatically.
As for your comment, a lot of OpenGL commands reserve the value 0 for disabling, but in this case I got it wrong (It’s usually the case only for glBind* calls). You would in fact use glDisableVertexAttribArray with the nth location in this case, making the 0th location perfectly acceptable.
Thanks for that!
Swiftless
Hi, I think in the line:
glEnableVertexAttribArray(0); // Disable our Vertex Array Object
it does not disable the vertex array object, instead it enables usage of the VertexAttribPointer on the 0th location. thus if we want to send a texture coordinate in each vertex, we can do:
glEnableVertexAttribArray(1). And we set the proper offet using the glVertexAttribPointer(1, …….)
The enabling or disabling of the vertex array object would be done via:
glBindVertexArray(0) or glBindVertexArray(id)
Hey Swiftless!
First of all: thanks this really good tutorial!
But now my problem:
After downloading and compiling this project I noticed, that if I resize the window, the square gets streched. I thought the glm:perspectiveMatrix function would do the same like glPerspective, but it doesn’t. Or am I doing something wrong? And how is it possible to change this, so that aspect ratio of the square stays the same while resizing?
Hi Caasoo,
At a guess the matrix isn’t being applied correctly to the scene, it might be worth double checking the shader is actually getting loaded as some people using the latest Visual studio 2010 have found it doesn’t load from the usual path.
Cheers,
Swiftless
Swiftless, thanks for the tut. Looking forward to the next one.
Couple of questions;
For some reason i get different result when debugging in VS2010 then running the .exe direct. In VS i get a big white box, when running the .exe i get a small black one. What concerns me is neither are red like yours?! Any idea on why this is happening?
Also, shouldn’t we be recalculating the projection matrix when in resizeWindow()? As the width and height we used in setupScene() have changed.
Keep up the good work sir.
Ah ok i know why i’m getting the white box while debugging, it doesn’t seem as though the shaders are being used when debugging. Downloading your project does the same, and also gives me a black box.
Does debugging in VS run the exe as if its a different location?
Hi JimmyDeemo,
I am also using VS2010 and am getting the same issue (In debug mode, the shader is not being loaded). Unfortunately I don’t know what is causing this and I haven’t really looked into it. As long as the shader files are in the same directory as the debug exe, it should load. Maybe sound debugging output when loading the shader will help find the problem?
Cheers,
Swiftless
Hi Swiftless,
I found the problem, if you go to your projects properties and go Configuration Properties -> Debugging, you will notice that your ‘Working Directory’ is set to the Project Dir, this is where all the source code. You need to change this to the Output Dir (i.e. Debug folder), and then make sure the shader’s are in there.
Alternatively what i have done is make a ‘bin’ folder, which has all my assets in including shaders, then changed the output directory to that folder. This allows me to keep the shaders in source control without adding in the Debug folder which technically is generated at build time.
Hope that helps your debugging.
Jimmy.
Thank you so much it worked I can see the colors now 🙂
My background comes out black, and there is no square. I’ve tried downloading your source from this project, which indicates it might be an issue with libraries or dll’s?
Hi Andrew,
If the actual background is black and not blue, then it sounds like something is horribly wrong with the creation of the OpenGL context. It should default back to OpenGL 2.1 if OpenGL 3.2 and up is not found. Do you have any problems with the older tutorials on the site?
Cheers,
Swiftless
Andrew, and Swiftless you might want to look at this too. I noticed that on my work machine my project was failing to draw anything. In the end i found out it was because my card drivers didn’t support OpenGL 3.2. So first thing i did was update those, and i would encourage you to do the same.
The problem was that the code didn’t drop to 2.1 as intended and it was because the check wasn’t failing despite 3.2 not being supported.
I would recommend to Swiftless to use the following code, instead of what’s in the first tut;
if (GLEW_VERSION_3_2)
{
//Create 3.2 context
}
else
{
//Fall back to 2.1
}
I found this dropped to 2.1 prior to my driver update, but the original code did not.
Hope that helps.
Thanks a lot for these tutorials! I’ve been looking a ton recently for some good OpenGL 3.x tutorials but there seems to be a giant lack of them.
Keep up the great work! I’m looking forward to your next tutorials!
So when the next tutorials can be expected ? 🙂
Hi LaskaB,
Being on holidays, I’m hoping to have out out this week depending on work. If all goes well, two even 😀
Cheers,
Swiftless
Hi Swfitless,
Really nice tutorial, how would you go about, for example, drawing a circle and two squares? Would this be done by creating a vbo for each object and attaching them all to the vao? Sorry if it’s a stupid question, I’m still a learner.
Thanks in advance.
Hi Speeb,
You can do this in a couple of ways. You could create a separate VBO for each shape and place each one in a separate VAO. Or you could place all the vertices in one VBO inside of one VAO and if you have created your array of vertices correctly, it will show up the same.
A VAO is only meant to hold one array of vertices and each other VAO is for per-vertex attributes.
Cheers,
Swiftless
Can we count on example about tessellation ?
Hey Persky,
Since I got an OpenGL 4.0 compatible graphics card, you sure can count on one.
Cheers,
Swiftless
Maybe i’m miss something, but why square color is red? It seems we don’t set it. Is red color – default color?
Sorry for bad english 🙂
Hi Evgeniy,
Good question. When we create the VBO for our vertex positions, that takes care of the in_Position attribute variable that is passed to our shader. However we also have an in_Color value which is unoccupied as we don’t have a VBO yet for that. The value red is caused by a value of 1.0 being picked up in the shader when there is nothing assigned.
In the next coming tutorial on coloring the square, we use another VBO to store the color of the square and the order of the VBO’s inside the VAO’s corresponds to the order we defined the in_Color and in_Position variables in the shader.cpp file.
Cheers,
Swiftless
Hei~ Swiftless. This is amazing tutorial. I just want to learn how to use OpenGL3.x. But when I get the code in this tutorial to run, I can see noting just a empty window with blue background. I can’t see the square in the window, I just copy your code. Can you check the code again, by the way, I use vs2008 which I think is not the problem.
And I look forward to the next tutorial so much, er.., if you have time.
Hi LuckyBean,
First off, make sure you have the fragment shader and vertex shader files included in the project in the same directory as the executable file you have compiled, and let me know how that goes.
Cheers,
Swiftless
My flaut…The code is all right, it works. I’m sorry.