Chances are good that if you decide to make OpenGL calls directly, you have already thought about performance and efficiency. Optimizing VRAM use with texture atlasing is a good way to do that. In this article, I will talk about how to create really optimized textures.
What is a texture atlas?
I won’t spend time here talking about what a texture atlas is or why you should use them. There are other sites that do a good job of that already.
Here’s the link to the Wikipedia page that also has links to whitepapers and tools.
Lazrhog Games wrote a blog post recently about their experiences with creating a texture atlas and how it applies to implementing them in Objective-C.
There is another great write-up by Gallant Games on how you can leverage atlases to reduce state changes.
A Real-World Example
What I want to do is talk about how to optimize the atlases you’re already using by showing you what a released game does and how it could be optimized.
Here’s an atlas from the recently released game Speedball 2: Evolution. (A great game if you haven’t played it yet. There’s a free version also available.)
This example is the medic droid:
Here, the animation frames are laid out in a grid system as you might expect. This makes the code easier because it assumes the size of each frame and sets the UVs accordingly.
I chose this particular image because it’s a good example of some gotchas when you create an atlas. I’ll go through each quirk one-by-one.
The first thing you’ll notice is the big, empty region on the bottom right. It’s a curious decision to do this because of the apparent waste of VRAM. It’s possible they are filling that space with something else when they load their textures (something I will describe in a future article), but it’s not likely.
The next thing to focus on is the wasted space between the frames, especially the yellow light glow. The way to fix that is to create a texture atlas of what I call “shrink wrapped” frames (others call it “bounding boxes”). The idea is to find the minimum size for each frame and store only that. I wrote my own tool years ago to do this for me, but there are other tools on the web that also do this. The result from my tool is shown below. I have modified the alpha channel so we can talk more about it.
It’s a lot smaller than the original, but what’s going on with that yellow light glow now? Why is it so tall? Well, when you inspect the alpha channel, you’ll notice some stray pixels in the bottom right corner.
It’s an oops from the artist but we’ll forgive them because it’s hard to notice that the alpha values are 1s and 2s. Those values compared to black are extremely hard to see.
The quickest way to fix that is to go into the original yellow light glow frame and set those bad alpha pixels to 0. There is another way to fix it, but let’s first talk about how it came to be a problem in the first place.
I said earlier that it’s really hard to see alphas of 1 and 2 against black. If you look at the edge of the shadow beneath each frame, you’ll see that it starts at 1 and moves towards 255 the more opaque the shadow gets (remember, we’re talking about the alpha channel here). Does it really need to be that subtle? If you were to start with, say, 8, would anyone notice? Would you gain anything by doing that? Let’s take a look.
Here’s the new alpha channel:
And here’s the result when applied to the frames (click it to see it at full size):
Can you see the difference? No, I can’t either. Actually I can. Look really close at the edge of the shadows on the right. They start with a value of 8 so they ‘pop’ a little bit. If I took my time and started with a lower value and smoothed out the other values going to 255, you wouldn’t notice any difference. The player certainly wouldn’t when it’s displayed in-game.
Here’s the atlas for the frames that have the new alpha channel applied:
The size is 499×138. When you compare that to the original atlas of 480×210, the savings are quite dramatic (just under 32%).
But wait, there’s still more we can do. If you are able to, creating a long strip would give you even less waste. Have a look at this (click it to see it at full size):
This image is 990×69; a savings of just over 32% from the original.
The first step in optimizing OpenGL ES textures is to atlas them. However, to really get more out of your atlases, you need to create concentrated atlases. Texture memory is at a premium on devices like the iPhone and iPad, so you need to do everything you can to make sure your game doesn’t struggle.