OpenGL:Tutorials:Using Vertex Buffer Objects
Vertex Buffer Objects is a faster way to render geometry in OpenGL. This tutorial assumes basic understanding of C++ and OpenGL. You might want to take a read of the article on Wikipedia so you get a better idea.
VBOs were first available in OpenGL 2.1 (as an ARB extension), and became part of core in 3.0. I'll be using the 3.0 function names in my example code.
Contents |
Overview
To create a VBO you:
- Prepare data in the correct format
- Create a VBO
- Bind the buffer
- Load the data
To render a VBO you:
- Bind the buffer
- Set buffer attributes
- Render using DrawArrays
Prepare the data
Vertex data needs to be given to OpenGL. There is multiple bits of data you need to send - coordinates, texture uvs, normals, etc. You can provide multiple buffers (one per data type) and bind them all, you can have one big buffer with all the cooridnates first, all the uvs second and all the normals third, or you can do it like this tutorial, with a large interleaved array.
For the interleaved array to work, you need a structure:
struct VBOvertex { float x, y, z; // Vertex float nx, ny, nz; // Normal float tx, ty; // Tex };
Even though this is a C++ tutorial, I'm using a C style structure because I don't need (or want) any of the class schematics.
You need to load your vertex data into the vertex array. The code below loads the vertex data from a class storing a loaded Wavefront .obj file (referenced by the obj variable). The exact implementation you use is entirely up to you.
Vertex * v; Vertex * vn; TexUV * t; Face * f; VBOvertex* vertexes = new VBOvertex[obj->faces.size() * 3]; // Prepare the array int j = 0; for (unsigned int i = 0; i < obj->faces.size(); i++) { f = &obj->faces.at(i); // First vertex in face v = &obj->vertexes.at(f->v1 - 1); vn = &obj->normals.at(f->n1 - 1); t = &obj->texuvs.at(f->t1 - 1); vertexes[j].x = v->x; vertexes[j].y = v->y; vertexes[j].z = v->z; vertexes[j].nx = vn->x; vertexes[j].ny = vn->y; vertexes[j].nz = vn->z; vertexes[j].tx = t->x; vertexes[j].ty = t->y; j++; // Second vertex in face v = &obj->vertexes.at(f->v2 - 1); vn = &obj->normals.at(f->n2 - 1); t = &obj->texuvs.at(f->t2 - 1); vertexes[j].x = v->x; vertexes[j].y = v->y; vertexes[j].z = v->z; vertexes[j].nx = vn->x; vertexes[j].ny = vn->y; vertexes[j].nz = vn->z; vertexes[j].tx = t->x; vertexes[j].ty = t->y; j++; // Third vertex in face v = &obj->vertexes.at(f->v3 - 1); vn = &obj->normals.at(f->n3 - 1); t = &obj->texuvs.at(f->t3 - 1); vertexes[j].x = v->x; vertexes[j].y = v->y; vertexes[j].z = v->z; vertexes[j].nx = vn->x; vertexes[j].ny = vn->y; vertexes[j].nz = vn->z; vertexes[j].tx = t->x; vertexes[j].ty = t->y; j++; }
This isn't the most efficient way to do things, but it does work.
Create VBO
You then create and load up the VBO with the data:
GLuint vboid; // Create the VBO glGenBuffers(1, &vboid); // Bind the VBO glBindBuffer(GL_ARRAY_BUFFER, vboid); // Load the buffer data glBufferData(GL_ARRAY_BUFFER, sizeof(VBOvertex) * obj->faces.size() * 3, vertexes, GL_STATIC_DRAW);
You will want to save the vboid somewhere in application memory for later reference. I have a class for each model, which contains the texture ids, vbo ids, etc for each frame of the animation (or one frame for a non-animated model), but you can store it however you want. You will also need to store the length of the VBO array.
The vertex data is copied to OpenGL memory space (either on the card or in ram), so we should free the vertex array we just created.
delete [] vertexes;
Rendering the VBO
- TODO: Finish