8. Optimizing for File Size and Performance 197
8.3 Quality versus Quantity: What’s Available and What’s It About?
When it comes to optimizing your textures, there is almost always a compromise re- quired. You cannot compress images without degradation in quality, but if you want your 20-MB texture to come down to 2 MB, that’s just what you are going to have to deal with. The trick is to try and build your game so that optimization is possible without being too noticeable, using tricks such as reducing the amount of alpha transparency in textures so that you can use a nonalpha compression system, or using texture atlases
8.3. Quality versus Quantity: What’s Available and What’s It About? 199
to put everything onto a single texture rather than several separate ones.
Select your texture in the Project window to bring up the texture settings in the Inspector win- dow as shown in Figure 8.1. As you plan and build out assets for your game (both 3D textures and UI textures), you should keep optimization in mind.
Wherever you can reuse a texture, wherever you can share a texture, or wherever you can use a solid tex- ture rather than an alpha transparency, you should.
In Unity iOS, there are a number of image com- pression settings available to you (although you may only end up using three or four of the main ones).
We will get to that in a moment, but for now, let’s take a look at the presets and options that Unity of- fers for textures.
Preset texture settings are organized into sever- al types. What Unity has done is provide those tex- ture types to help you to easily get the right settings for the right applications. With a texture highlighted in the Project window, the first drop-down in the texture importer (within the Inspector window) is Texture Type. Texture types are:
y Texture. This is intended for use with a texture applied to a mesh. There are a lim- ited number of modifiable properties under this texture type.
y Normal map. Under this setup, Unity will automatically turn off all color channels so that the assigned texture becomes suitable for real-time normal mapping.
y GUI. Using the GUI type will set up your texture to be as crisp as possible and per- fect for display through an orthographic camera for GUIs or HUDs.
y Reflection. This method of reflection is often called cube mapping. It is used with a specific shader to simulate reflective sur- faces, such as chrome.
y Cookie. Use this if your texture is going to be a Cookie for your lights.
y Lightmap. This offers parameters specifically relevant to textures used for lightmapping.
y Advanced. Under this setting, you will have access to all of the available param- eters that the Unity texture importer has to offer.
Figure 8.1. The image importer in the Inspector win- dow of the Unity editor.
Note that some texture types only provide access to particular properties of the tex- ture importer in an effort to remain authentic to the overall theme’s parameters. For ex- ample, the texture texture type only allows you to change the Alpha, Max size, and Format values, whereas the advanced type allows you to change a lot more. The For- mat drop-down, within the texture texture type, has only three options, whereas the advanced type has no less than 15.
The compression format you choose from the Format drop-down of the texture importer will have a huge impact on the size of your image and how much RAM it will use once it gets to the iOS device. There are a number of different compression options available, but before we get to those, let’s take a quick look at how images are dealt with in Unity and what potential gotchas there may be.
The physical size of textures makes a huge difference to their uncompressed size because data is stored about every pixel that goes to make up an image. Mipmapping also increases texture sizes by around 33%, as it creates different versions of your tex- ture to be used at different distances from the camera. The color depth determines how much color data we need to store for each pixel, which means that if you can use smaller textures with a lower color depth (16 bit rather than 32 bit), you can substantially lower the image size.
Compression Memory consumption (bits per pixel)
RGB 16 bit 2 bpp
RGB 32 bit 4 bpp
RGBA 16 bit 2 bpp
RGBA 32 bit 4 bpp
PVRTC 2 bpp
Alpha PVRTC 4 bpp
Total texture size = width × height × bpp.
Note that the size of an image saved on a disk (the file size as reported by the finder or Windows Explorer) can differ wildly due to the amount of memory that the same image takes up once it is imported into the Unity engine. For example, if you have an 18- KB .png texture, that very same texture could turn into a 1-MB (or more) image once it is in Unity. The reason for this is that textures are only in a compressed state when they are on disk. As they are loaded into memory, they are stored in an uncompressed state, resulting in a dramatically larger memory footprint than the compressed originals.
When it comes to compression settings, it will really depend on the actual applica- tion and type of image you are using. For GUI and menus, I try to avoid using compres- sion at all because of the artifacts it causes. For in-game graphics, the more compressed graphics I can get away with, the better, but there are times when you need to use an uncompressed format to avoid degradation in the image quality. In those cases, you should always try to use 16-bit compression settings and see if it is good enough be- fore committing to 32-bit settings. Obviously, 16-bit images are half the size of 32-bit images, so the savings can be considerable and the definition much better than a com- pressed format.
8.3. Quality versus Quantity: What’s Available and What’s It About? 201
8.3.1 Draw Calls and Their Importance
It is no secret that large textures can eat up your available memory pretty quickly, es- pecially on mobile platforms where memory is limited. For that reason, we need to try every trick we can to preserve memory without compromising too much in quality.
One effective method is to try to use as few textures as possible. By doing so, you will not only save memory but also reduce draw calls, resulting in a more optimized game experience.
The most important method that the 3D modeler should employ to help keep draw calls under control is to reduce the number of materials in a scene and to share mate- rials across different meshes where possible. Try to avoid using multiple textures for a single mesh; instead, share the same image for all parts. Every material on a single mesh counts for a new draw call, so if you have four materials on a single mesh, it is the equivalent of drawing four meshes.
By sharing materials, the CPU can also communicate with the rendering hardware or software more efficiently because it doesn’t have to send out material information for every item in the environment. In Unity, this process is referred to as batching and it comes in two flavors: static (available only to Unity Pro users) and dynamic. Batch- ing costs in the vertex count, but the overhead is usually a lot less than that of several separate meshes generating several separate draw calls. We’ll talk about this more in Section 8.6.
8.3.2 Textures and the Importance of Texture Atlases
It is no secret that large textures can eat up your available memory pretty quickly, es- pecially on mobile platforms where memory is limited. For that reason, we need to try every trick we can to preserve memory without compromising too much in quality. One effective method is to try to use as low a number of textures as possible. By doing so, you will not only save memory but also reduce draw calls, resulting in a more optimized game experience.
Building 3D environments using texture atlases is not a new concept, and game artists have been using them for a long time. It takes a little more thought than simply putting textures onto objects as you go, but in the long run, the extra work on texturing could make or break the performance of the final game.
To reiterate the point made earlier in Section 8.3.1, each material is a new draw call, so if you have a single mesh that requires four separate textures, it is as though you are drawing the same object four times. Use a single texture atlas, and the information to draw everything on that mesh only needs to be sent once—a single draw call (Figure 8.2).
For 3D games, you will most likely just use a tool like Gimp1 to put together your atlases; however, if you need more than just a few images on an atlas, you may want to use some custom software. To help you build complex texture atlases, there are several software solutions available, both paid and free.
One of the most commonly used atlas generators is TexturePacker.2 Texture- Packer takes a sequence of images and puts them all onto one single, large texture atlas. This is ideal for 2D games, where you might have a number of frame-by-frame
1 http://www.gimp.org/
2 http://www.texturepacker.com
animations or situations where your graphics have been exported as lots of separate pieces (Figure 8.3).
8.3.3 Fill Rate and Its Impact on Performance
Processor speeds are getting better and devices are no doubt getting faster at rendering more complex scenes, but we must still take into account the impact of transparencies on performance and how fill rate impacts the amount of CPU we need to use up during the draw process.
When the iPad first came onto the scene, one of the challenges developers were faced with was getting games to render at double the size. The miniscule 128-MB RAM coupled with a processor containing a fraction of the power of a $200 laptop meant that games that were rendering just fine at 320 × 480 resolution began to struggle at 1024 × 768. Drawing transparent images was always expensive (iOS processor-wise), but when faced with ren- dering them at such a large scale, it was enough to grind otherwise sound environments down to a crawl.
The retina display means that newer iPhones and iPods actually render double the amount of pixels than their predecessors. The 320 × 480 is now 640 × 960 for the same-sized screen, which looks great, but means more work for the renderer. The iPad3 screen resolution is 2048 × 1536. Avoid using too many transparent textures, particu- larly large-sized transparent textures such as vignettes, frames around the screen, or any large elements that demand transparency.
Figure 8.2. An example of a texture atlas.
8.5. Why Audio Can Make or Break Your Game 203
Due to the nature of compression, images made up of solid blocks of color com- press much better than images with lots of different colors in them. Antialiased edges rarely compress as well as solid edges and you may find that antialiased-edged transpar- ent images often need to be stored in 16- or 32-bit uncompressed formats to retain any level of quality. Work with your artist or designer to experiment early on with different styles and settings to reach the best result with the minimum impact on memory usage.