Wednesday, April 4, 2012

Part 9: Texturing and Compression

For this post, I'm going to show you how to use texturetool, which comes bundled with the iOS SDK. This tool is used to compress textures so that they take less space in memory using the PVR texture compression format. You can compress your texture to use 2 bits per pixel without completely destroying the image, which is pretty amazing. Of course, there's different levels of compression, but I'll be using this one.

Also, this tool allows you to generate mipmaps offline, and GLKTextureLoader will automatically detect them. After having created the compressed textures, I will load them inside our application. I will then add a texture coordinate for each vertex in our vertex array and use them to sample the texture in our fragment shader.

The texture I'll be using is this one:

Square Texture. Original size 512 x 512.

I found it somewhere on the web some time ago, and I don't know who the author is. I will mention you if someone can find the source.

If you don't want to give yourself any trouble, it would be very important for the image to have a size that is a power of 2 (PoT). This image is a PNG file with a size of 512 = 2^9. It's a pretty big image (434 KB), but with the compression, it'll shrink down a lot.

To use texturetool, open your terminal. By default, you should be located in your Home Folder when you open it. From this location, the texturetool executable should be at the same path for everyone. You will need the path to the texture image, and you will also need to provide the path of the output compressed image, which can be the same as the input path. This is the bash command I use to generate the compressed PVR texture:

You can read the short and sweet texturetool reference for more information, but basically -m generates the mipmaps, -e compresses the texture in the given format. It's extremely simple and easy to use, and you can even create bash scripts to compress multiple textures in one call.

The resulting file has a size of 88 KB, so you reduced it by a factor of almost 5, and you also have all the mipmaps in there! Now all you need to do is import the resulting PVR file to your project.

First, you'll need to first add a property called texture of type GLKTextureInfo. And here's the code to load the texture in memory, which you can append to the ViewDidLoad method:

As a side note, the texture will be flipped upside down. There are many ways to draw the texture in its original orientation, for example you could flip the texture coordinates on the y axis, but since this is a square, I'm just going to forget about it and draw the texture upside down.

Here's the vertex array with the added texture coordinates (we use normalized values for texture coordinates):

Next, you need to change your attributes and the stride, and add an attribute to send the texture coordinates to your fragment shader. In the ViewDidLoad method, locate where you manipulate the attributes and change the code to the following:

In the DrawInRect method, you also need to add this before the call do glDrawElements to bind the texture before drawing:

Your shaders now need to use those attributes. Here's the code for the shaders:

The vertex shader now takes the coordinates and and dumps them in a varying variable. Those coordinates will then be interpolated and we can use them in the fragment shader. In the fragment shader, we use a new type of variable which is a sampler. With the texture2D call, we sample the texture at the coordinates give by the varying FragmentTexCoord0.

And boom!


andrei0077 said...
This comment has been removed by the author.
Gabriel Gohier-Roy said...

I'm going to guess that the code doesn't appear when Git is down, so that might have happened just now.

andrei0077 said...

>I'm going to guess that the code doesn't appear when
Git is down, so that might have happened just now.

Yes, Its right.

Joseph Humfrey said...


I had problems getting textures to load :-( Did you have this at all?

Error Domain=GLKTextureLoaderErrorDomain Code=12 "The operation couldn’t be completed. (GLKTextureLoaderErrorDomain error 12.)" UserInfo=0x111bc250 {GLKTextureLoaderErrorKey=Image decoding failed}

Gabriel Gohier-Roy said...

I'm not exactly sure what the problem might be, and definitely never ran into that error message. It's most probably a problem with the format of the image, but I can't be certain since it's been a while since I've played with this example.53