Rendering sprites from spritesheet with OpenGL?

Imagine the following scenario: you have a set of sprite lists of RPG characters in PNG format, and you want to use them in an OpenGL application.

Individual characters have a size (usually) of 16 by 24 pixels (i.e. 24 pixels in height) and can be of any width and height, without leaving indentation. Sort of

Terra, from Final Fantasy VI
(source: kafuka.org )

I already have code to define an integer clipping rectangle based on the frame index and size:

int framesPerRow = sheet.Width / cellWidth; int framesPerColumn = sheet.Height / cellHeight; framesTotal = framesPerRow * framesPerColumn; int left = frameIndex % framesPerRow; int top = frameIndex / framesPerRow; //Clipping rect width and height are obviously cellWidth and cellHeight. 

Execution of this code with frameIndex = 11, cellWidth = 16, cellHeight = 24 will return the clipcorp (32, 24)-(48, 48) assuming that it is opposite to the width / height on the right / bottom.

Actual question

Now, given the clipping rectangle and the X / Y coordinate for placing the sprite, how do I draw this in OpenGL? The presence of a zero coordinate in the upper left corner is preferred.

+5
source share
4 answers

You should start thinking in the "texture space" where the coordinates are in the range [0, 1].

So if you have a sprite sheet:

 class SpriteSheet { int spriteWidth, spriteHeight; int texWidth, texHeight; int tex; public: SpriteSheet(int t, int tW, int tH, int sW, int sH) : tex(t), texWidth(tW), texHeight(tH), spriteWidth(sW), spriteHeight(sH) {} void drawSprite(float posX, float posY, int frameIndex); }; 

All you have to do is send the vertices and vertices of the texture in OpenGL:

  void SpriteSheet::drawSprite(float posX, float posY, int frameIndex) { const float verts[] = { posX, posY, posX + spriteWidth, posY, posX + spriteWidth, posY + spriteHeight, posX, posY + spriteHeight }; const float tw = float(spriteWidth) / texWidth; const float th = float(spriteHeight) / texHeight; const int numPerRow = texWidth / spriteWidth; const float tx = (frameIndex % numPerRow) * tw; const float ty = (frameIndex / numPerRow + 1) * th; const float texVerts[] = { tx, ty, tx + tw, ty, tx + tw, ty + th, tx, ty + th }; // ... Bind the texture, enable the proper arrays glVertexPointer(2, GL_FLOAT, verts); glTexCoordPointer(2, GL_FLOAT, texVerts); glDrawArrays(GL_TRI_STRIP, 0, 4); } }; 
+8
source

Franks solution is already very good.

Just a (very important) side element, as some of the comments are suggested differently.

Please never use glBegin / glEnd. Never tell someone to use it.

The only time you need to use glBegin / glEnd in your first OpenGL program.

Arrays are not harder to process, but ...

  • ... they are faster.
  • ... they will still work with new versions of OpenGL.
  • ... they will work with GLES.
  • ... loading them from files is much easier.
+6
source

I assume you are learning OpenGL, and only need to get this to work somehow. If you need raw speed, there are shaders and vertex buffers and all kinds of neat and complex things.

The easiest way is to load PNG into the texture (provided that you have the ability to load images into memory, you need htat), and then draw it using the square corresponding to the corresponding coordinates of the texture (they go from 0 to 1 with a floating point , so you need to divide by the width or height of the texture).

Use glBegin (GL_QUADS), glTexcoord2f (), glVertex2f (), glEnd () for the easiest (but not the fastest) way to draw.

To make the top left zero, either use gluOrtho () to adjust the view matrix differently than regular GL (find the documents for this function, set top to 0 and bottom to 1 or screen_height if you want integer coordinates) or just change your loop draw and just do glVertex2f (x / screen_width, 1-y / screen_height).

There are better and quicker ways to do this, but this is probably one of the easiest if you are learning raw OpenGL from scratch.

0
source

Suggestion, if possible. I use SDL to load my textures, so I did this: 1. I loaded the texture 2. I determined how to split the sprite into separate sprites. 3. I divided them into separate surfaces 4. I make a texture for each (I have a class of sprites to manage them). 5. Free the surfaces. This takes longer (obviously) at boot but pays later. Thus, it is much simpler (and faster) since you only need to calculate the texture index you want to display and then display. Then you can scale / translate it as you like and call up a display list to display it as you wish. Or you can do it directly, or it works :)

0
source

All Articles