New to OpenGL, need some guidence on 2D Game / sprites and VBO

Hello everyone!

I decided to start a new project and make a game and I want to keep it 2D sprite based only. So I started learning today how to use OpenGL
and was able to get a hang of it very qucikly.

Now I really just have few questions about using VBOs and using them for sprites

  1. I know VBO is considered the fastest when it comes to rendering, but is it the best option for a 2D only game?
  2. Would I be able to transform sprites when and if I need to (Rotation and scale)?
  3. What about transforming sprites based on a world matrix or 2D camera?

And my last question!

I created the below code which uses a VBO to render the sprites I have in an array of sprite objects.
Is this the right way / best way of rendering quads with textures on them using a VBO? Is there a better way?


//Temp Class for sprites
	public class Sprite
	{
		public float x;
		public float y;
		public Texture texture;
	}

//Function used to "run" the game
	public void run()
	{

//Create an array of sprites and init them
		Sprite [] sprite = new Sprite[2];
		sprite[0] = new Sprite();
		sprite[0].x = 20;
		sprite[0].y = 20;
		sprite[0].texture = square; //previously loaded texture using the slick texture and texture loader
		
		sprite[1] = new Sprite();
		sprite[1].x = 125;
		sprite[1].y = 125;
		sprite[1].texture = smile; //previously loaded texture using the slick texture and texture loader
		
		//Create the amount of vertexs and sie
		final int amountOfVertex = 4;
		final int vertexSize = 2; / 2 for x and y cord only

//Create the vertex buffer
		FloatBuffer vertexData = BufferUtils.createFloatBuffer(amountOfVertex * vertexSize);

//create and init the texture coord buffer for mapping a texture to a quad
		FloatBuffer texCoordData = BufferUtils.createFloatBuffer(amountOfVertex * vertexSize);
		texCoordData.put(new float[]{
				0.0f, 0.0f,
				1.0f, 0.0f,
				1.0f, 1.0f,
				0.0f, 1.0f});
		texCoordData.flip();
		
		//Set up the vertex buffer
		int vboVertexHandle = glGenBuffers();
		glBindTexture(GL_TEXTURE_2D, square.getTextureID());
		glBindBuffer(GL_ARRAY_BUFFER, vboVertexHandle);
		glBufferData(GL_ARRAY_BUFFER, vertexData, GL_STATIC_DRAW);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		
//Set up the texture coord buffer
		int texCoordHandle = glGenBuffers();
		glBindTexture(GL_TEXTURE_2D, texCoordHandle);
		glBindBuffer(GL_ARRAY_BUFFER, texCoordHandle);
		glBufferData(GL_ARRAY_BUFFER, texCoordData, GL_STATIC_DRAW);
		glBindBuffer(GL_ARRAY_BUFFER, 0);

		
		//Init OpenGL		
		while(!Display.isCloseRequested())
		{
			//used for user input
			gamepad.pollInput();
			
//Clear the buffer
			glClear(GL_COLOR_BUFFER_BIT);
			
//Run though the sprite list and draw them to the screen
//Is this the part im doing right? Thanks :D
			for(int i = 0; i < 2; i++)
			{
			
//Create the verteex buffer data based on the sprite to draw
				vertexData.put(new float[]{
						sprite[i].x, sprite[i].y,
						sprite[i].x + sprite[i].texture.getImageWidth(), sprite[i].y,
						sprite[i].x + sprite[i].texture.getImageWidth(), sprite[i].y + sprite[i].texture.getImageHeight(),
						sprite[i].x, sprite[i].y + sprite[i].texture.getImageHeight()});
				vertexData.flip();
				
//bind the texture to use and handle the rest of the drawing
				glBindTexture(GL_TEXTURE_2D, sprite[i].texture.getTextureID());
				glBindBuffer(GL_ARRAY_BUFFER, vboVertexHandle);
				glBufferData(GL_ARRAY_BUFFER, vertexData, GL_STATIC_DRAW);
				glBindBuffer(GL_ARRAY_BUFFER, 0);
				
				glBindBuffer(GL_ARRAY_BUFFER, vboVertexHandle);
				glVertexPointer(vertexSize, GL_FLOAT, 0, 0L);
				
				glBindBuffer(GL_ARRAY_BUFFER, texCoordHandle);
				glTexCoordPointer(vertexSize, GL_FLOAT, 0, 0L);
							
				glEnableClientState(GL_VERTEX_ARRAY);
				glEnableClientState(GL_TEXTURE_COORD_ARRAY);
				glDrawArrays(GL_QUADS, 0, amountOfVertex);
				glDisableClientState(GL_TEXTURE_COORD_ARRAY);
				glDisableClientState(GL_VERTEX_ARRAY);
			}
			
		//Update and sync it to 60 FPS
			Display.update();
			Display.sync(60);
		}

Any code examples would be greatly appreciated!

I would recommend using LibGDX. The SpriteBatch class does all the VBO assembly and packing for you, and like the name implies, batches them for bulk uploads to the GPU.

Even if you don’t use LibGDX, I’d at least take a look at the code for SpriteBatch to see how it works.

  1. It’s a good option. So is vertex arrays. Or geometry shaders, if supported.
  2. Yes. Generally scaling would be done on CPU by simply specifying different vertex positions. Rotation could also be done on CPU, by transforming each vertex before placing it in your array. Alternatively, you could send the model transform as a uniform matrix to the shader, which allows you to do the transformation on the GPU. Maybe a system comprised of both would be ideal.
  3. Easy as pie if you’re using a shader.

LibGDX’s sprite batch actually uses simple vertex arrays. When it comes to streaming data every frame, they are almost as fast as VBOs, or on some drivers even faster. There are also some implications on Android.

Your code looks a little strange. Why are you calling glBindTexture on a buffer ID? Creating a new float array every frame seems like a waste. And you’re using VBOs for such a small amount of data that it won’t be very effective. Further, your system isn’t very flexible; what if you wanted to add many more textures?

You should also think about texture atlases; i.e. using a single texture comprised of many sprites, in order to reduces texture binds and batch renders.

I wrote a “bare bones” intro to LWJGL sprite batching that I’d recommend taking a look at:

It introduces you to textures, shaders, a simple orthographic projection matrix, vertex arrays, and a simple SpriteBatch implementation. Once you are comfortable with those ideas, you could try implementing VBOs to see if it’s any more performant.

Hey its GiraffeTap22 I forgot my pass and the email I used… anyway…

Thank you both very much for the replies! But if I can I would like to make my game from scratch.

Davedes I looked at the link to your classes. It gave me a few ideas, but I have a few questions about the whole thing

Could you explain the shaders program to me? I am completely confused about them, because when I hear the word shader I think about 3D models and lighting. Something not used in a 2D game.

Also, this maybe stupid of me asking, but what is the difference between drawRegion and draw?

Is drawRegion just drawing sprites that fall in a certain area?

Also I went back and changed my code up a bit. Its seems I can render 10000 sprites in about 180 - 215 milliseconds according to a simple execution run time check. I get the execution time by rendering all the of the sprites once and then closing down the program.

Is there anything else I could do?


public class Sprite
	{
		public float x;
		public float y;
		public Texture texture;
	}
	
	public GameWindow()
	{
			
		//init the game window and OpenGl
		initGameWindow();
		initOpenGL();
		
		//Load some textures
		square = loadTexture("square.png");
		smile = loadTexture("smile.png");
			
		gamepad = new GamepadsController();
		//lastFrame = getTime();
	}
	
	public void run()
	{
		//Create an array of sprite objects
		Sprite [] sprite = new Sprite[10000];	
		
		//Create a new random gen
		Random gen = new Random();		
		
		//create sprites and place them randomly based on the screen size
		for(int i = 0; i < sprite.length; i++)
		{
			sprite[i] = new Sprite();
			sprite[i].x = gen.nextInt(SCREENWIDTH) + 1;
			sprite[i].y = gen.nextInt(SCREENHEIGHT) + 1;
			
			//Set the texture 
			if(i % 2 == 0)
				sprite[i].texture = square;
			else
				sprite[i].texture = smile;
		}
	
		//set a place holder for the current texture to be rendered
		currentTexture = sprite[0].texture;
		
		//Set up the vertex buffer
		final int amountOfVertex = 4;
		final int vertexSize = 2;

		FloatBuffer vertexData = BufferUtils.createFloatBuffer(amountOfVertex * vertexSize);

		FloatBuffer texCoordData = BufferUtils.createFloatBuffer(amountOfVertex * vertexSize);
		texCoordData.put(new float[]{
				0.0f, 0.0f,
				1.0f, 0.0f,
				1.0f, 1.0f,
				0.0f, 1.0f});
		texCoordData.flip();
		
		int vboVertexHandle = glGenBuffers();
		glBindTexture(GL_TEXTURE_2D, currentTexture.getTextureID());
		glBindBuffer(GL_ARRAY_BUFFER, vboVertexHandle);
		glBufferData(GL_ARRAY_BUFFER, vertexData, GL_STATIC_DRAW);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		
		int texCoordHandle = glGenBuffers();
		glBindBuffer(GL_ARRAY_BUFFER, texCoordHandle);
		glBufferData(GL_ARRAY_BUFFER, texCoordData, GL_STATIC_DRAW);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		
		float [] quadPosition = new float[amountOfVertex * vertexSize];
		
		for(int i = 0; i < (amountOfVertex * vertexSize); i++)
			quadPosition[i] = 0.0f;
		
		//Init OpenGL		
		while(!Display.isCloseRequested())
		{
			//poll data
			gamepad.pollInput();
			
			//Clear
			glClear(GL_COLOR_BUFFER_BIT);
			
			//Draw all the sprites
			for(int i = 0; i < sprite.length; i++)
			{
				
				//set the texture for the quad and bind it
				currentTexture = sprite[i].texture;
				currentTexture.bind();
				
				//Test movement
				//sprite[i].x += 1;
				
				//clear the vertex data
				vertexData.clear();
				
				//Set up the position of the quad
				quadPosition[0] = sprite[i].x;
				quadPosition[1] = sprite[i].y;
				quadPosition[2] = sprite[i].x + sprite[i].texture.getImageWidth();
				quadPosition[3] = sprite[i].y;
				quadPosition[4] = sprite[i].x + sprite[i].texture.getImageWidth();
				quadPosition[5] = sprite[i].y + sprite[i].texture.getImageHeight();
				quadPosition[6] = sprite[i].x;
				quadPosition[7] = sprite[i].y + sprite[i].texture.getImageHeight();
				
				vertexData.put(quadPosition);
				vertexData.flip();

				//Bind and draw the Quad
				glBindBuffer(GL_ARRAY_BUFFER, vboVertexHandle);
				glBufferData(GL_ARRAY_BUFFER, vertexData, GL_STATIC_DRAW);
				glBindBuffer(GL_ARRAY_BUFFER, 0);
					
			
				glBindBuffer(GL_ARRAY_BUFFER, vboVertexHandle);
				glVertexPointer(vertexSize, GL_FLOAT, 0, 0L);
				
				glBindBuffer(GL_ARRAY_BUFFER, texCoordHandle);
				glTexCoordPointer(vertexSize, GL_FLOAT, 0, 0L);
							
				glEnableClientState(GL_VERTEX_ARRAY);
				glEnableClientState(GL_TEXTURE_COORD_ARRAY);
				glDrawArrays(GL_QUADS, 0, amountOfVertex);
				glDisableClientState(GL_TEXTURE_COORD_ARRAY);
				glDisableClientState(GL_VERTEX_ARRAY);
			}
			
			Display.update();
			Display.sync(60);
			
		}
		
		Display.destroy();

	}

Thanks again!

The specs on the computer I’m working with

Specs [Dell Laptop] -
OS : Win 7 Home Premium Service Pack 1
Processor : Intel Core Duo t9550 2.66 GHz
Ram : 4 Gb DDR3
Video Card : ATI mobility Radeon HD 4670

Haha I just posted a tutorial on how to make a basic spritebatcher…follow davedes as his is more robust but if you want to stay away from shaders look at what I just posted.

drawRegion I think is for drawing only a region of the texture…basically texture atlases and stuff.

Idk what your system specs are but you should be at way higher then 10k sprites in 180-215 milliseconds.

I recommend getting a batcher working using glBegin/glEnd first so you understand what type of abstraction you want to use. Then try out vertex arrays/VBO.

I should have probably posted my system specs! I would like to use VBOs and the above code is my attempt at them,
but are saying that I should have gotten a way less execution time?

Specs [Dell Laptop] -
OS : Win 7 Home Premium Service Pack 1
Processor : Intel Core Duo t9550 2.66 GHz
Ram : 4 Gb DDR3
Video Card : ATI mobility Radeon HD 4670

I will look into your example as well!

Shaders can be used for 2D or 3D purposes – OpenGL doesn’t know what kind of game you’re making.

drawRegion is used for sprite sheets or “texture atlases” – for example, instead of loading multiple PNG files into different textures, you create a single texture which contains all of your sprites. That way, you don’t need to bind a new texture for each sprite, and you can also batch all of your sprites into the same render call.

You didn’t really change or fix your example… It’s still rendering one sprite at a time – the whole point of a sprite batcher is to render many sprites in one render call.

I’d strongly recommend trying to learn the basics, like how to load and draw a texture without using SlickUtil, before tackling something like VBOs. Check out Stumpy’s SpriteBatcher guide, and read through the lwjgl-basics library I’ve posted to try and get a better grasp on what’s going on.

Like I said, you don’t really need VBOs for a simple sprite renderer… Just stick with vertex arrays.

I guess I really didn’t understand the concept of a sprite batcher then.

Correct me if I’m wrong. Ok so when we make a sprite batcher, we are really just calculating the vertex, texture, and color data for an X amount of quads (sprites) and then
placing them in giant arrays.

Then when we reach our X amount of quads to handle at one time we render them using a single bind call

So basically this :


myBatcher.Begin();

myBatcher.draw(sprite1, 50, 50);
myBatcher.draw(sprite2, 12, 9);

myBatcher.End();

Would start up the batcher Then add sprite 1 to the batcher and plan to draw it at x cord 50 and y cord 50.
Then add sprite 2 to the batcher and set up its position. Finally the batcher’s end function is called which
actually renders everything and does any clean up

Yep, that’s the basic idea. Calling drawSprite puts that sprite’s data into the array, and then increases the index pointer for the next call. Your array (or ByteBuffer) is a fixed size (generally something really big), and you render it all at once.

You only “flush” or “render” the batch when:

  • You reach the end of your array (index pointer > max)
  • You need to change the texture; i.e. render the batch with the old texture, then bind the new one
  • The user explicitly requested you to “flush” or render the batch, i.e. if they wanted to change the blend function.

Awesome! One part does part concern me a little, you said I would call my render
function if I changed textures, but wouldn’t that have the potential to slow me down?

What I mean is that if sprite 1 is texture A and sprite 2 is texture B then code below



myBatcher.Begin();

myBatcher.draw(sprite1, 50, 50);
myBatcher.draw(sprite2, 12, 9);

myBatcher.End();

is really like doing



myBatcher.Begin();
myBatcher.draw(sprite1, 50, 50);
myBatcher.End();

myBatcher.Begin();
myBatcher.draw(sprite2, 50, 50);
myBatcher.End();

Or am I completely wrong?

@Phased he may be referring to an earlier post that was long.

@OP: not quite. It can depend on your implementation on how you handle thing but in general it would be more of.


myBatcher.Begin();
myBatcher.draw(sprite1, 50, 50);
myBatcher.End();

myBatcher.draw(sprite2, 50, 50);
myBatcher.End();


The glBegin is normally not expensive as it only sets things up. It is the filling of they arrays/buffers that will slow you down the most. Then if you are using Texture Atlases, flushing to the gpu.

But yes the concept is like that in the respect that once you bind a new texture, everything currently in the batch needs to be sent to the gpu before you can start using the new texture otherwise your sprites will use the wrong image.

Yes, I was. ;D

Yes – this is because (given the way it’s currently set up) you can only have one texture bound at a time. That’s why I suggested texture atlases to reduce texture binds and increase batch count. Every 2D game should use texture atlases to improve performance, where applicable.

Unless you have card that supports texture arrays. ;D