Texture Formats: Best practices

Textures are at the core of any game. When you create a game with Sparrow or any other 2D framework, it will consist almost exclusively of very simple objects (quads) with textures mapped onto them. Even the most simple games can look stunning when they are using beautiful textures.

This article should teach you the most important facts about textures — things you have to take care of, file formats you can choose from, and tools to help you. The information it contains holds true not only for Sparrow, but for any game or framework that uses OpenGL for its rendering.

Overview

OpenGL textures have a peculiarity: their side lengths must be powers of two (POT). That means that OpenGL textures must have dimensions that are in the following range:

1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048

So, a texture may be 512 pixels wide and 8 pixels high, but not, say, 511 by 9 pixels.

“Nonsense”, you might think, “I'm using different dimensions all the time, without any problems!” Agreed, but only because Sparrow hides this limitation from you. When you load a non-POT texture, it creates the next biggest POT-texture behind the scenes and draws your texture into that.

Thus, an image that is 350×180 pixels will lead to the creation of a 512×256 texture in the background, because that's the smallest POT-texture that can hold it.

The only problem with this is that the texture will need as much memory as the big POT-texture, even if most of that texture is not used. In games with a limited number of textures, this might be OK, but you'll quickly reach the limits of graphics memory if you load all your textures that way.

However, it's hard to deny that typical objects in your game don't care much about that limitation — you can't have only objects with POT-dimensions! But fear not, other developers have been struggling with that for a long time and have come up with a smart solution for that problem.

Texture Atlas aka "Sprite Sheet"

A texture atlas is one huge texture, normally with POT-dimensions for the reasons stated above, that contains lots and lots of smaller textures. It is also known by the name “sprite sheet”.

Here is how such a texture atlas looks like:

The trick is to have OpenGL use this big texture instead of the small ones, and to map only a part of it to the quad you are drawing. If the images are arranged in a smart way (more on that later), this will lead to a very efficient memory usage, wasting as little space as possible.

Since this is done so often, Sparrow makes using a texture atlas as easy as possible. Once you have loaded the atlas, it does not feel any different from working with the texture files themselves.

// load the atlas
SPTextureAtlas *atlas = [SPTextureAtlas atlasWithContentsOfFile:@"atlas.xml"];
 
// display a sub-texture
SPTexture *pacmanTexture = [atlas textureByName:@"pacman"];
SPImage *pacmanImage = [SPImage imageWithTexture:pacmanTexture];

In many games, you can fit almost all of your textures in one or two texture atlases. In that case, the easiest way to manage them is to load all of them right at the beginning of your game, and access them throughout your code. The Media class shows you one easy way to do that.

There's one thing you have to be aware of: iOS devices have different limits on the maximum texture size, so it's wise to stay below those limits.

≤ iPhone 3G, iPod Touch 2G1024×1024 pixels
≥ iPhone 3GS, iPod Touch 3G, iPad 12048×2048 pixels

Creating a Texture Atlas

Assembling all those images in a big texture would be a lot of work if you had to do it manually. Thankfully, there are tools that help you with that process!

  • Sparrow's own atlas generator. Have a look at this tutorial for a description of how to use it, and how it can help you to make games for retina displays.
  • Texture Packer: a great commercial tool with tons of features like PVR output, texture cropping and dithering, and much more. Grab it here.

File formats

There are different file formats available which can hold your textures. The format you are using can have influence on memory consumption.

PNG

Since most textures in 2D games contain an alpha channel, PNG is the most widely used image format for textures, as it provides decent file compression and contains an 8 bit alpha channel. It's also the format that is easiest to use — almost every image editor can read and write PNG format.

It is, however, also the format that takes up the most space in graphics memory.

Graphics Memory

A PNG image stores 4 channels for every pixel: red, green, blue and alpha, each with 8 bit (that makes 256 values per channel). It's easy to calculate how much space a 512×512 pixel texture takes up:

512 x 512 pixels x 4 bytes = 1,048,576 bytes = 1 MB

Quite a lot for such a small texture, right? Beware that PNG's built-in file compression does not help: the image has to be decompressed before OpenGL can handle it.

Nevertheless: if your textures fit into graphics memory with that format — go on and use PNG. Sparrow's sample game PenguFlip, for example, works fine with PNG textures. They are very easy to use and work with, so I recommend sticking to PNG as long as possible.

However, there might come a moment in the development of your game where your memory consumption is higher than what is available on the device.

This is the right time to look at the PVR image format.

PVR

The graphics chips of all current iDevices are developed by PowerVR. This hardware has a built-in texture format that it understands and decodes on the fly. Now that's the important part: a PVR texture will take up almost exactly as much space in graphics memory as it does on your hard drive! That's the main difference to PNG textures, and that's the reason why PVR textures are so special.

The downside is that you probably can't display or save that format in the image editor of your choice. You have to use special tools for that.

There's so much to say about PVR textures that I've moved their description to a separate page:

PVR Textures

JPEG

I don't think I need to tell you much about the JPEG image format. That must be one of the most popular file formats of all times — you'll find it all over the place when you're browsing through the internet, and it's very likely that your collection of family photos is saved in JPEG.

JPEG is a lossy compression format. That means that your images will lose quality when you save them with JPEG (yes, even when you're using the maximum quality setting). In exchange, the compression rate is very good. However, what I said about the PNG format holds true here, too: the GPU doesn't “speak” JPEG, so the images need to be decompressed before being sent to graphics memory.

The main problem with JPEG, though, is that it does not have an alpha channel. For most of the textures in your game, this will be a show-stopper, because it means that you can only save rectangular objects in JPEG format.

For that reason, the recommended way to use compressed, lossy textures on iOS is PVRTC. Not only does it have an alpha channel, but it can be used in its compressed form by the GPU.

Conclusion

That information should give you an overview about what you have to consider when you are working with textures in Sparrow or OpenGL ES. The next time you're running out of texture memory, there are no more excuses: do something about it! 8-)

  tutorials/texture_formats_best_practices.txt · Last modified: 2011/09/01 08:51 by daniel
 
Powered by DokuWiki