GusOnGames, adding the texturesGusOnGames, adding the textures

Using vertex buffers to speed up drawing

In the previous article about heightmaps, I mentioned that I used Vertex Buffer Objects to speed up drawing.

Being part of the OpenGL standard since version 3.2 buffers are a powerful mechanism to let you store vertex data, texture data, shader data and so on. The key to their speed is that they are stored in GPU memory, which provides fast and efficient access. Before the standardization of the buffers all these data had to reside in system memory making complex scenes and effects slow. We had to rely on extensions that were not always available.

In this article we will discuss the buffer objects as they are used in the HEIGHTMAP example where we store vertex data to speed the drawing of the landscape.

So what are these buffers?

Buffers are powerful mechanism that allows you to store vertex, pixel, texture data, inputs for shader execution, or the output of different shader stages. They are stored in GPU memory resulting in high execution speed. Rendering an object no longer requires sending its geometry to the GPU, a process that is very slow.


Creating Buffers

Creating and using them is fairly easy. First you should call 'glGenBuffers' to create names for your buffers. The buffers are actually created when they are used for the first time. The actual first use is when you load the buffer with data, using 'glBufferData' or equivalent functions.

When you want to draw using the data stored in the buffer, you enable the use of buffers, bind the buffer with the 'glBindBuffer' function, tell OpenGL the offset to the start of the data and call 'glDrawArrays' to draw.

Here is the code that creates and loads vertex, normals and texture coordinates buffers.

void clf_mesh::build_buffers()
{
	// Generate And Bind The Vertex Buffer
	glGenBuffers( 1, &m_nVBOVertices );				// Get A Valid Name
	glBindBuffer( GL_ARRAY_BUFFER, m_nVBOVertices );		// Bind The Buffer 
	// load The Data
	DWORD len = m_nVertexCount*sizeof(clf_vector3D);
	// allocate a buffer for the vertices and the normals, filled with seros
	glBufferData( GL_ARRAY_BUFFER, len*2, 0, GL_STATIC_DRAW );	// (vertices and normals)
	// copy the vertices in the first half
	glBufferSubData(GL_ARRAY_BUFFER, 0, len, m_pVertices);
	// and the normals in the rest of the buffer
	glBufferSubData(GL_ARRAY_BUFFER, len, len, m_pNormals);
	glBindBuffer( GL_ARRAY_BUFFER, 0 );				// Release The Buffer 

	if (m_nTextureId > 0)
	{
		// Generate And Bind The Texture Coordinate Buffer
		glGenBuffers( 1, &m_nVBOTexCoords );		// Get A Valid Name
		glBindBuffer( GL_ARRAY_BUFFER, m_nVBOTexCoords );	// Bind The Buffer
		// load The Data
		glBufferData( GL_ARRAY_BUFFER, m_nVertexCount*sizeof(clf_point2D), m_pTexCoords, GL_STATIC_DRAW );
		glBindBuffer( GL_ARRAY_BUFFER, 0 );			// Release The Buffer
	}
}

Using the data stored in a buffer to draw is easy as well. First you tell OpenGL to use buffers and then you specify which one by binding it. The next step is to specify the use of the buffer, weather it is vertices, normals or texture coordinates. Finally you draw by calling 'glDrawArrays' and you have drawn your geometry. Remember to release the buffers when you are done by telling OpenGL to stop using buffers when drawing.

And here is the code that uses the buffers to draw the terrain based on the heightmap.

void clf_mesh::render()
{
	glEnable(GL_TEXTURE_2D);
	glEnableClientState( GL_VERTEX_ARRAY );				// Enable Vertex Arrays
	glEnableClientState(GL_NORMAL_ARRAY);
	if (m_nTextureId > 0)
		glEnableClientState( GL_TEXTURE_COORD_ARRAY );		// Enable Texture Coord Arrays

	// Set Pointers To Our Data
	glBindBuffer( GL_ARRAY_BUFFER, m_nVBOVertices );
	glNormalPointer(GL_FLOAT, sizeof(clf_vector3D), (void*)(m_nVertexCount*sizeof(clf_vector3D)));
	glVertexPointer( 3, GL_FLOAT, sizeof(clf_vector3D), (char *) NULL );	// Set The Vertex Pointer To The Vertex Buffer

	if (m_nTextureId > 0)
	{
		glBindBuffer( GL_ARRAY_BUFFER, m_nVBOTexCoords );
		glTexCoordPointer( 2, GL_FLOAT, sizeof(clf_point2D), (char *) NULL );	// Set The TexCoord Pointer To The TexCoord Buffer
		glBindTexture( GL_TEXTURE_2D, m_nTextureId );				// Bind The Texture
	}

	// render
	glDrawArrays( GL_TRIANGLES, 0, m_nVertexCount );			// Draw All Of The Triangles At Once

	// Disable Pointers
	glDisableClientState( GL_VERTEX_ARRAY );				// Disable Vertex Arrays
	glDisableClientState(GL_NORMAL_ARRAY);					// disable normal arrays
	if (m_nTextureId > 0)
		glDisableClientState( GL_TEXTURE_COORD_ARRAY );			// Disable Texture Coord Arrays
}

Several techniques using frame buffer objects (objects similar to the vertex buffer objects). Those are buffers in he video memory used to do background drawing for effects like drawing on textures or motion blur. In future articles we will cover these techniques of special efects.

Finally you can download the complete sample from here

GusOnGames is member of Obscure Horizons group
Increase your website traffic with Attracta.com