Thiago
Thiago

Reputation: 471

OpenGL performance issue

I'm writing a 2D RPG using the LWJGL and Java 1.6. By now, I have a 'World' class, which holds an ArrayList of Tile (interface with basic code for every Tile) and a GrassTile class, which makes the use of a Spritesheet.

When using Immediate mode to draw a grid of 64x64 GrassTiles I get around 100 FPS and do this by calling the .draw() method from each tile inside the ArrayList, which binds the spritesheet and draws a certain area of it (with glTexCoord2f()). So I heard it's better to use VBO's, got a basic tutorial and tried to implement them on the .draw() method.

Now there are two issues: I don't know how to bind only a certain area of a texture to a VBO (the whole texture would be simply glBindTexture()) so I tried using them with colours only. That takes me to second issue: I got only +20 FPS (120 total) which is not really what I expected, so I suppose I'm doing something wrong. Also, I am making a single VBO for each GrassTile while iterating inside the ArrayList. I think that's kind of wrong, because I can simply throw all the tiles inside a single FloatBuffer.

So, how can I draw similar geometry in a better way and how can I bind only a certain area of a Texture to a VBO?

Upvotes: 0

Views: 390

Answers (2)

Wroclai
Wroclai

Reputation: 26925

So, how can I draw similar geometry in a better way...

Like @Ian Mallett described; put all your vertex data into a single vertex buffer object. This makes it possible to render your map in one call. If your map get 1000 times bigger you may want to implement a camera solution which only draws the vertices that are being shown on the screen, but that is a question that will arise later if you're planning on a significantly bigger map.

...and how can I bind only a certain area of a Texture to a VBO?

You can only bind a whole texture. You have to point to a certain area of the texture that you want to be mapped.

Every texture coordinate relates to a specific vertex. Every tile relates to four vertices. Common tiles in your game will share the same texture, hence the 'tile map' name. Make use of that. Place all your tile textures in a texture sheet and bind that texture sheet. For every new 'tile' you create, check whether the area is meant to be air, grass or ground and then point to the part of the texture that corresponds to what you intend.

Let's say your texture area in pixels are 100x100. The ground area is 15x15 from the lower left corner. Follow the logic above explains the example code being shown below:

// The vertexData array simply contains information
// about a tile's four vertices (or six 
// vertices if you draw using GL_TRIANGLES).

mVertexBuffer.put(0, vertexData[0]);
mVertexBuffer.put(1, vertex[1]);
mVertexBuffer.put(2, vertex[2]);
mVertexBuffer.put(3, vertex[3]);
mVertexBuffer.put(4, vertex[4]);
mVertexBuffer.put(5, vertex[5]);
mVertexBuffer.put(6, vertex[6]);
mVertexBuffer.put(7, vertex[7]);
mVertexBuffer.put(8, vertex[8]);
mVertexBuffer.put(9, vertex[9]);
mVertexBuffer.put(10, vertex[10]);
mVertexBuffer.put(11, vertex[11]);

if (tileIsGround) {
   mTextureCoordBuffer.put(0, 0.0f);
   mTextureCoordBuffer.put(1, 0.0f);

   mTextureCoordBuffer.put(2, 0.15f);
   mTextureCoordBuffer.put(3, 0.0f);

   mTextureCoordBuffer.put(4, 0.15f);
   mTextureCoordBuffer.put(5, 0.15f);

   mTextureCoordBuffer.put(6, 0.15f);
   mTextureCoordBuffer.put(7, 0.0f);
} else { /* Other texture coordinates. */ }

You actually wrote the solution. The only difference is that you should upload the texture coordinates data to the GPU.

Upvotes: 2

geometrian
geometrian

Reputation: 15397

This is the key:

I am making a single VBO for each GrassTile while iterating inside the ArrayList.

Don't do this. You make a VBO once, and then you update it if necessary. Making textures, VBOs, shaders, is the slowest possible use of OpenGL--no wonder you're getting problematic framerates--you're doing it O(n) times, each frame.

I think that's kind of wrong, because I can['t?] simply throw all the tiles inside a single FloatBuffer.

You only gain performance when you batch draw calls. This means that when you draw your tiles, you should draw all of them at once with one VBO.

//Initialize
Make a single VBO (or two: one for vertex, one for texture
coordinates, whatever--the key point is O(1) VBOs).
Fill your VBO with ALL of your tiles' data.

//Main loop
while (true) {
    Draw the VBO with a single draw call,
    thus drawing all your tiles all at once.
}

Upvotes: 2

Related Questions