Go to OpenGL Home
Last time we prepared a class, called GameModels.h which handles triangle creation. Now it’s time to continue and implement those methods in the GameModels.cpp file. Before that, we should talk a little bit how OpenGL will handle our triangle vertices and send them to the vertex shader. I’m sure that this is going to be a little bit hard to understand all this concepts that I’m going to present, especially if you see them for the first time. Maybe you have to read the tutorial a few times and also read other sources too.
Short story: We have to put our triangle vertices in the GPU memory and then, in the rendering loop you have to send them to the vertex shader.
In this tutorial we will talk about:
- Vertex Buffer Objects(VBO), how vertices are stored into the GPU memory.
- Vertex Array Objects, the importance of them.
In the last part, part III we will:
- Connect vertex attributes (position) with attribute inputs from vertex shader
- Modify our vertex shader
- GameModel.cpp source code
The C++ code written here to show how to create VBO and VAO will be used in the next tutorial in CreateTriangleModel method from GameModels class created in previous tutorial. I strongly suggest you to carefully read about VBO and VAO before you jump right into code, because these are one of the most important concepts you have to understand in OpenGL .
1. Vertex Buffer Object (VBO)
These 3 vertices must be stored somehow in the graphics memory and tell our GPU that ” Hey, this is my vertex data that you will need to draw it later in the loop“. As you can see, for performance reasons, we will create our triangle before we enter the rendering loop. It’s really, really bad to create objects in the render loop.
We can store our 3 vertices into a buffer which is just an array of memory. To be able to manipulate and use this buffer whenever we want, OpenGL allows us to create a Vertex Buffer Object(VBO) which is just a container for our buffer. When we create this container, OpenGL reserves a name(ID) which can be access anytime. Because OpenGL is a finite-state machine you have to: create, bind, fill the VBO and delete it when you don’t need it anymore. In the picture below I illustrated how this process works.
//this code will be used in the next tutorial in CreateTriangleModel method //triangle vertices on stack std::vector<VertexFormat> vertices; //specify vertex positions vertices.push_back(VertexFormat(glm::vec3(-0.5, -0.5, 0.0))); vertices.push_back(VertexFormat(glm::vec3( 0.0, 0.5, 0.0))); vertices.push_back(VertexFormat(glm::vec3( 0.5, -0.5, 0.0))); unsigned int vbo; //our vbo handler glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo);//bind to context glBufferData(GL_ARRAY_BUFFER, sizeof(VertexFormat) * vertices.size(), &vertices, GL_STATIC_DRAW);
Let’s talk just a little bit about each step:
- Step 1: Create VBO. We can use vbo variable in our program. This acts just like a handler. First parameter tells how many buffers we need to create. In our case is just one.
- Step 2: Bind VBO to a Binding Point(or Target). In our case the binding point is GL_ARRAY_BUFFER. We need to do this to bind our buffer to the OpenGL context. There are other types of binding points.
- Step 3: We allocate memory at that binding point.
- Size is given by the number of vertices multiplied by the sizeof our VertexFormat structure created in the previous tutorial which contains just the position. Now GPU knows exactly how much memory needs to allocate for this object.
- Data will copy the actual data which in our case is an array of VertexFormat into the buffer.
- Usage tells how to use the buffer. Usually you go with GL_STATIC_DRAW which tells that this buffer is created and modified once. There are 9 types of usage. You can read more about them here.
- Step 4. Delete VBO. What you create you have to destroy it too. Destroy VBO when you don’t use it anymore.
2. Vertex Array Object (VAO)
After the VBO is all set you can go ahead and draw the vertices in the rendering loop by binding the VBO again and call glVertexAttribPointer(), glEnableVertexAttribArray() methods to fill the correct data from the buffer to the vertex shader. But first, let’s imagine that we have 5 buffers for a complex model, we will have to bind and call those functions for every frame. Now if you have a complex scene with several objects with 4 or 5 buffers, would be hard to keep track of every buffer and attribute that you need to bind in order to draw them correctly.
In OpenGL 3.0 a new type of object was introduced called Vertex Array Object which can keep tracks and remembers multiple VBOs. In the rendering loop we just bind the VAO and that’s all. For example, for 3 objects we can call just 3 binding functions instead of 15 methods.
Let’s see how we do this in code:
//this code will be used in the next tutorial in CreateTriangleModel method //VAO unsigned int vao; //our VAO handler glGenVertexArrays(1, &vao); //create VAO container and get ID for it glBindVertexArray(vao); //bind to OpenGL context //now that VAO is binded to context we can reference our buffer(s) unsigned int vbo; //our vbo handler glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo);//bind to context glBufferData(GL_ARRAY_BUFFER, sizeof(VertexFormat) * vertices.size(), &vertices, GL_STATIC_DRAW);
In modern OpenGL is encourage to use a VAO even just for one VBO. Not using VAO is a little bit deprecated. Even from a performance point of view it’s better to use VAOs, because it doesn’t have to check for valid params in these methods glVertexAttribPointer(), glEnableVertexAttribArray() .
Don’t use VAO in scenarios where you have to change vertex format and rebind buffers frequently. The whole point of a VAO whould be eliminated. Also don’t get confused with the binding point name GL_ARRAY_BUFFER which we used in the VBO creation.
In the next tutorial we will see how to connect with vertex shader and draw the triangle.