Texture Compression
Texture mapping can add incredible realism to any 3D rendered scene, with a minimal cost in vertex processing. One drawback to using textures, however, is that they require a lot of memory to store and process. Early attempts at texture compression were crudely storing textures as JPG files and decompressing the textures when loaded before calling glTexImage. These attempts saved disk space or reduced the amount of time required to transmit the image over the network (such as the Internet), but did nothing to alleviate the storage requirements of texture images loaded into graphics hardware memory.
Native support for texture compression was added to OpenGL with version 1.3. Earlier versions of OpenGL may also support texture compression via extension functions of the same name. You can test for this extension by using the GL_ARB_texture_compression string.
Texture compression support in OpenGL hardware can go beyond simply allowing you to load a compressed texture; in most implementations, the texture data stays compressed even in the graphics hardware memory. This allows you to load more texture into less memory and can significantly improve texturing performance due to fewer texture swaps (moving textures around) and fewer memory accesses during texture filtering.
Compressing Textures
Texture data does not have to be initially compressed to take advantage of OpenGL support for compressed textures. You can request that OpenGL compress a texture image when loaded by using one of the values in Table 9.1 for the internalFormat parameter of any of the glTexImage functions.
Table 9.1. Compressed Texture Formats
Compressed Format |
Base Internal Format |
GL_COMPRESSED_ALPHA |
GL_ALPHA |
GL_COMPRESSED_LUMINANCE |
GL_LUMINANCE |
GL_COMPRESSED_LUMINANCE_ALPHA |
GL_LUMINANCE_ALPHA |
GL_COMPRESSED_INTENSITY |
GL_INTENSITY |
GL_COMPRESSED_RGB |
GL_RGB |
GL_COMPRESSED_RGBA |
GL_RGBA |
Compressing images this way adds a bit of overhead to texture loads but can increase texture performance due to the more efficient usage of texture memory. If, for some reason, the texture cannot be compressed, OpenGL uses the base internal format listed instead and loads the texture uncompressed.
When you attempt to load and compress a texture in this way, you can find out whether the texture was successfully compressed by using glGetTexLevelParameteriv with GL_TEXTURE_COMPRESSED as the parameter name:
GLint compFlag; . . . glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED, &compFlag);
The glGetTexLevelParameteriv function accepts a number of new parameter names pertaining to compressed textures. These parameters are listed in Table 9.2.
Table 9.2. Compressed Texture Parameters Retrieved with glGetTexLevelParameter
Parameter |
Returns |
GL_TEXTURE_COMPRESSED |
The value 1 if the texture is compressed, 0 if not |
GL_TEXTURE_COMPRESSED_IMAGE_SIZE |
The size in bytes of the compressed texture |
GL_TEXTURE_INTERNAL_FORMAT |
The compression format used |
GL_NUM_COMPRESSED_TEXTURE_FORMATS |
The number of supported compressed texture formats |
GL_COMPRESSED_TEXTURE_FORMATS |
An array of constant values corresponding to each supported compressed texture format |
GL_TEXTURE_COMPRESSION_HINT |
The value of the texture compression hint (GL_NICEST/GL_FASTEST) |
When textures are compressed using the values listed in Table 9.1, OpenGL chooses the most appropriate texture compression format. You can use glHint to specify whether you want OpenGL to choose based on the fastest or highest quality algorithm:
glHint(GL_TEXTURE_COMPRESSION_HINT, GL_FASTEST); glHint(GL_TEXTURE_COMPRESSION_HINT, GL_NICEST); glHint(GL_TEXTURE_COMPRESSION_HINT, GL_DONT_CARE);
The exact compression format varies from implementation to implementation. You can obtain a count of compression formats and a list of the values by using GL_NUM_COMPRESSED_TEXTURE_FORMATS and GL_COMPRESSED_TEXTURE_FORMATS. To check for support for a specific set of compressed texture formats, you need to check for a specific extension for those formats. For example, nearly all implementations support the GL_EXT_texture_compression_s3tc texture compression format. If this extension is supported, the compressed texture formats listed in Table 9.3 are all supported, but only for two-dimensional textures.
Table 9.3. Compression Formats for GL_EXT_texture_compression_s3tc
Format |
Description |
GL_COMPRESSED_RGB_S3TC_DXT1 |
RGB data is compressed; alpha is always 1.0. |
GL_COMPRESSED_RGBA_S3TC_DXT1 |
RGB data is compressed; alpha is either 1.0 or 0.0. |
GL_COMPRESSED_RGBA_S3TC_DXT3 |
RGB data is compressed; alpha is stored as 4 bits. |
GL_COMPRESSED_RGBA_S3TC_DXT5 |
RGB data is compressed; alpha is a weighted average of 8-bit values. |
Loading Compressed Textures
Using the functions in the preceding section, you can have OpenGL compress textures in a natively supported format, retrieve the compressed data with the glGetCompressedTexImage function (identical to the glGetTexImage function for uncompressed textures), and save it to disk. On subsequent loads, the raw compressed data can be used, resulting in substantially faster texture loads. Be advised, however, that some vendors may cheat a little when it comes to texture loading in order to optimize texture storage or filtering operations. This technique will work only on fully conformant hardware implementations.
To load precompressed texture data, use one of the following functions:
void glCompressedTexImage1D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLint border, GLsizei imageSize, void *data); void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, void *data); void glCompressedTexImage3D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, Glsizei imageSize, GLvoid *data);
These functions are virtually identical to the glTexImage functions from the preceding chapter. The only difference is that the internalFormat parameter must specify a supported compressed texture image. If the implementation supports the GL_EXT_texture_compression_s3tc extension, this would be one of the values from Table 9.3. There is also a corresponding set of glCompressedTexSubImage functions for updating a portion or all of an already-loaded texture that mirrors the glTexSubImage functionality from the preceding chapter.
Texture compression is a very popular texture feature. Smaller textures take up less storage, transmit faster over networks, load faster off disk, copy faster to graphics memory, allow for substantially more texture to be loaded onto hardware, and generally texture slightly faster to boot! Don't forget, though, as with so many things in life, there is no such thing as a free lunch. Something may be lost in the compression. The GL_EXT_texture_compression_s3tc method, for example, works by stripping color data out of each texel. For some textures, this results in substantial image quality loss (particularly for textures that contain smooth color gradients). Other times, textures with a great deal of detail are visually nearly identical to the original uncompressed version. The choice of texture compression method (or indeed no compression) can vary greatly depending on the nature of the underlying image.