OpenGL:Tutorials:Using Vertex Buffer Objects

From GPWiki
Jump to: navigation, search

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.

Overview

To create a VBO you:

  1. Prepare data in the correct format
  2. Create a VBO
  3. Bind the buffer
  4. Load the data

To render a VBO you:

  1. Bind the buffer
  2. Set buffer attributes
  3. 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