Last Ray of Hope Home of Kaluriel Hargrove

30Jul/11Off

Using Vertex Array Objects

Amy Pond <3

With my recent post about what iOS devices supported what extensions, I noticed a lot of people got to my website trying to work out how to use Vertex Array Objects (VAO) with the GL_OES_vertex_array_object extension.

They're easy to use so I thought I would write up a quick how-to use them. There is a limitation with the VAO however, it cannot be shared between contexts and can be problematic for multithreading.

If you plan to use one, either generate and create for each context you will be using it on. Or generate and create on the context you will be mostly using it on. It might be a mistake, since it says they didn't want to create the first non-shared named object in OpenGL ES, however at the same time they say no to sharing.

Like with any OpenGL object, first a name must be generated for the object, make sure you always initialise your name variables to 0, since this refers to a non-object.

GLuint gVAO = 0;
 
//
//
int main()
{
	//
	GenerateVAO();
	CreateVAO();
 
	//
	do
	{
		BindVAO();
		DrawSomething();
		UnbindVAO();
	}
	while( 1 );
 
	//
	DestroyVAO();
}
 
//
//
void GenerateVAO()
{
	// Generate Name for VAO
	glGenVertexArraysOES( 1, &gVAO );
}
 
//
//
void DestroyVAO()
{
	if( gVAO )
	{
		glDeleteVertexArraysOES( 1, &gVAO );
		gVAO = 0;
	}
}

I've also put the syntax in there for deleting the VAO when you do not need it anymore.

Now for filling in the VAO. The VAO is basically a block of memory for storing vertex data schematic. For fixed function, the schematic includes information about data type, count, stride, buffer offset and whether it is enabled. This is for every pointer, glVertexPointer, glNormalPointer, glColorPointer, glTexCoordPointer, etc.

And for the shader program pipeline, it contains the schematic for each glVertexAttribPointer.

//
//
void CreateVAO()
{
	// Bind VAO
	glBindVertexArrayOES( gVAO );
 
	// Bind Vertex Buffer
	glBindBuffer( GL_ARRAY_BUFFER, gSomeVBO );
 
	// Fill in the VAO with our vertex schematic
	if( hasShaders )
	{
		glEnableVertexAttribArray( 0 );
 
		glVertexAttribPointer( 0,			// Attribute bind location
				       3,			// Data type count
				       GL_FLOAT,		// Data type
				       GL_FALSE,		// Normalise this data types?
				       sizeof(float) * 3,	// Stride to the next vertex
				       0 );			// Vertex Buffer starting offset
	}
	else
	{
		glEnableClientState( GL_VERTEX_ARRAY );
 
		glVertexPointer( 3,			// Data type count
				 GL_FLOAT,		// Data type
				 sizeof(float) * 3,	// Stride to the next vertex
				 0 );			// Vertex Buffer starting offset
	}
 
	// Unbind VAO
	glBindVertexArrayOES( 0 );
}

 

You'll notice that when I am finished with with the VAO, I unbind it. This is because any changes to the vertex pointers will override any we have set within this function, so to protect the VAO we have just filled in, we unbind it.

When using a VAO, you will not need to bind the GL_ARRAY_BUFFER again the next time you use the VAO, since whenever you make a call to gl*Pointer() or glVertexAttribPointer(), the vertex buffer is stored with the vertex schematic. Because of this, you can have multiple streams of data from different vertex buffers.

//
//
void CreateVAO()
{
	// Bind VAO
	glBindVertexArrayOES( gVAO );
 
	// Vertex Buffer 1
	{
		// Bind Vertex Buffer
		glBindBuffer( GL_ARRAY_BUFFER, gSomeVBO1 );
 
		// Fill in the VAO with our vertex schematic
		if( hasShaders )
		{
			//
			glEnableVertexAttribArray( 0 );
 
			glVertexAttribPointer( 0,			// Attribute bind location
					       3,			// Data type count
					       GL_FLOAT,		// Data type
					       GL_FALSE,		// Normalise this data types?
					       sizeof(float) * 5,	// Stride to the next vertex
					       0 );			// Vertex Buffer starting offset
 
			//
			glEnableVertexAttribArray( 1 );
 
			glVertexAttribPointer( 1,			// Attribute bind location
					       2,			// Data type count
					       GL_FLOAT,		// Data type
					       GL_FALSE,		// Normalise this data types?
					       sizeof(float) * 5,	// Stride to the next vertex
					       sizeof(float) * 3 );	// Vertex Buffer starting offset
		}
		else
		{
			//
			glEnableClientState( GL_VERTEX_ARRAY );
 
			glVertexPointer( 3,			// Data type count
					 GL_FLOAT,		// Data type
					 sizeof(float) * 5,	// Stride to the next vertex
					 0 );			// Vertex Buffer starting offset
 
			//
			glClientActiveTexture( GL_TEXTURE0 );
			glEnableClientState( GL_TEXTURE_COORD_ARRAY );
 
			glTexCoordPointer( 2,						// Data type count
					   GL_FLOAT,					// Data type
					   sizeof(float) * 5,				// Stride to the next vertex
					   (const GLvoid *)(sizeof(float) * 3) );	// Vertex Buffer starting offset
		}
	}
 
	// Vertex Buffer 2
	{
		// Bind Vertex Buffer
		glBindBuffer( GL_ARRAY_BUFFER, gSomeVBO2 );
 
		// Fill in the VAO with our vertex schematic
		if( hasShaders )
		{
			glEnableVertexAttribArray( 2 );
 
			glVertexAttribPointer( 2,			// Attribute bind location
					       2,			// Data type count
					       GL_FLOAT,		// Data type
					       GL_FALSE,		// Normalise this data types?
					       sizeof(float) * 2,	// Stride to the next vertex
					       0 );			// Vertex Buffer starting offset
		}
		else
		{
			glClientActiveTexture( GL_TEXTURE1 );
			glEnableClientState( GL_TEXTURE_COORD_ARRAY );
 
			glTexCoordPointer( 2,			// Data type count
					   GL_FLOAT,		// Data type
					   sizeof(float) * 2,	// Stride to the next vertex
					   0 );			// Vertex Buffer starting offset
		}
	}
 
	// Unbind VAO
	glBindVertexArrayOES( 0 );
}

 

In the code above, the first texture coordinate set is interlaced with the position data (offset by the sizeof three floats), and is bound using the first vertex buffer (gSomeVBO1). But the second texture coordinate is contained within a second vertex buffer (gSomeVBO2).

Finally, using the VAO is easy.

//
//
void BindVAO()
{
	glBindVertexArrayOES( gVAO );
}
 
//
//
void UnbindVAO()
{
	glBindVertexArrayOES( 0 );
}

The call to glBindVertexArrayOES() with the VAO object name is just like calling everything we did in the create function. The only limitation is that one VAO is needed per vertex buffer, even if you have the same vertex schematic.

However, this is a great place for optimisation by sharing the same vertex buffer for vertex data with the same schematic. Then whenever you make a call to glDrawArrays() you can specify which vertex to start from. glDrawElements() is a little more complicated, you will need to increase all your elements in your index buffer so they point to the correct position within the shared buffer.

Or alternatively for glDrawElements(), you could share the same index buffer, and just specify the index buffer offset.

26Jul/11Off

Website Updates

I made some updates to a couple of entries I did a while ago. Fixed an error in my syntax where I calculated the reciprocal, but then divided by the reciprocal instead of multiplying.

I also changed the entries to show equivalent functions instead of my bad descriptions of what the parameters do.

iOS4 Vector and Matrix Math

iOS4 Vector and Matrix Math: Part 2

24Jul/11Off

iOS OpenGL ES Extension Support

Supported iOS OpenGL ES ExtensionsThis weekend I was adding improved extension support to the Athena engine for iOS. There is a page on the Apple site that tells what device has what extensions but it hasn't been updated since iOS 4.2.

So I ran through each version of GLES on a 1st Generate iPod Touch (MBX), an iPhone 4 (SGX) and an iPad 2 (A5), printing the returned string from glGetString( GL_EXTENSIONS ) for each device.

There are a some new extensions in iOS5, some of which are iPad2 specific. But you can see from the image, each newer device supports all the previous extensions.

I haven't had chance to run it on an iPad1 yet, so it will be interesting to see if there is any difference. I've only wrote a rough idea of what version of what SDK is required to use them.

Anything with '***' does not need the extension to work and has an implementation for that version of OpenGL ES.

For more information on each extension, most of them can be viewed here. Some of the newer ones in the EXT namespace are viewable on the what's new page of the beta for iOS5, though the page calls them by the APPLE namespace still.

16Jul/11Off

Really bad transistor joke

What did the NPN transistor say when it heard someone whisper 'hey'?

'HELLO!!!!!'

Tagged as: No Comments
9Jul/11Off

WordPress Category Order

I'm currently doing a general cleanup of the blog this morning. I noticed that WordPress doesn't allow you to order your categories, a quick Google found Category Order which works great, and has a nice Ajax interface.

I've also added some of the iOS apps that I've worked on, it's weird how sometimes I come by stuff in my backup drive that I have just plain forgotten I have worked on.

For example, when i was in Sixth Form I was part of a team that made a robot for fighting in a local event Cyber Scraps (very similar to Robot Wars), setup by the nearby nuclear power plant Sellafield. I can't find any pictures of our robot.

9Jul/11Off

Tap Zoo, the game that punishes you for not playing

I haven't played Tap Zoo for a while, I was hoping I would eventually reach the max level limit to see what happens to the content with these kinds of games, however I eventually gave up, Smurf's I gave up on a few month ago after I missed collecting crops and would have needed to harvest 40+ crops only to replant them.

Tap Zoo I logged back in to find out that it punishes you for not playing. Any animals you were breeding will become sick and the only way to free up the slot is to pay for stars.

Back at the beginning of the game you were given stars on level up, however after about 10 levels that stops. What a bunch of idiots, you do not punish people for coming back to a game, you're suppose to reward them to keep them back.

A recent new called call Tiny Tower is starting to take over the top of the top grossing charts. The reason, in my opinion would be the fact you do not have to pay to gain the ingame bonus tokens, you just need to play.

The more often you play, the faster you will accumulate money, and you gain actual iOS achievement as well.

It also reminds me of SimTower, which is how I came by it in the first place. Just like all these Facebook games, it have little tasks to gain you small amounts of currency as well, delivering people to floors. These not only give you money, but give you people to rent apartments so you can give them jobs.

Occasionally, VIPs will turn up that will knock off time for restocking, building, and increase people arriving at your stores to buy things. As I have said previously, the key to making a successful Facebook game, is to make a game that works like an ordinary game but you do not have to play as well.

EVE Online uses this formula.