So far, we’ve dealt exclusively with single-texture images. That is, whenever we draw a textured polygon, the polygon is painted with a single 1D or 2D image. This is fine for some scenes, but animated displays often need various levels of detail depending on the distance from the viewer. For example, when walking through a virtual room, you might want a high-resolution image of a picture close up, but only the outline at a distance.
OpenGL supports textures with multiple images, called mipmapped textures. Mipmapping selects the texture image closest to the screen resolution for a polygon. Loading mipmapped textures takes slightly longer than standard textures, but the visual results are impressive. In addition, mipmapped textures can improve display performance by reducing the need for GL_LINEAR image filters.
Mipmapped textures are defined by providing a specific level parameter for each image. For the ROY-G-BIV texture in the previous example, you would use the following:
static unsigned char roygbiv_image0; static unsigned char roygbiv_image1; static unsigned char roygbiv_image2; static unsigned char roygbiv_image3; static unsigned char roygbiv_image4; glNewList(RainbowTexture = glGenLists(1), GL_COMPILE); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); glTexImage1D(GL_TEXTURE_1D, 0, 3, 16, 0, GL_RGB, GL_UNSIGNED_BYTE, roygbiv_image0); glTexImage1D(GL_TEXTURE_1D, 1, 3, 8, 0, GL_RGB, GL_UNSIGNED_BYTE, roygbiv_image1); glTexImage1D(GL_TEXTURE_1D, 2, 3, 4, 0, GL_RGB, GL_UNSIGNED_BYTE, roygbiv_image2); glTexImage1D(GL_TEXTURE_1D, 3, 3, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, roygbiv_image3); glTexImage1D(GL_TEXTURE_1D, 4, 3, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, roygbiv_image4); glEndList();
The image levels are specified in the first parameter to glTexImage1D(). The level 0 image is your primary, highest-resolution image for the texture. The level 1 image is half the size of the primary image, and so forth. When drawing polygons with a mipmapped texture, you need to use one of the minification filters (GL_TEXTURE_MIN_FILTER) in Table 12-3.
The GL_LINEAR_MIPMAP_NEAREST and GL_LINEAR_MIPMAP_LINEAR filters can be very expensive in terms of display performance. GL_NEAREST_MIPMAP_NEAREST is roughly equivalent to GL_NEAREST in performance, but generally produces much better results. Mipmap images are chosen by comparing the size of the polygon as it will be drawn on the screen, to the sizes of the mipmap images.
To make your life a bit easier, the OpenGL utility library (GLU32.LIB) provides two functions that automatically generate mipmapped images based on a single, high-resolution texture. In the following code, the gluBuild1DMipmaps and gluBuild2DMipmaps functions take the place of glTexImage1D and glTexImage2D:
/* 1D texture */ glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); gluBuild1DMipmaps(GL_TEXTURE_1D, 3, 8, 0, GL_RGB, GL_UNSIGNED_BYTE, roygbiv_image); /* 2D texture */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); gluBuild2DMipmaps(GL_TEXTURE_2D, 3, info->bmiHeader.biWidth, info->bmiHeader.biHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, rgb);
Because the gluBuild1DMipmaps and gluBuild2DMipmaps functions create images from one image, the appearance of some textured images may not be accurate. It’s like drawing text characters at different sizes—scaling the bitmaps doesn’t always generate good-looking results! When you run into this sort of problem, generate your mipmap images manually.
Our project for this chapter is a terrain viewing program that takes advantage of some of the texture-mapping features we have discussed. With this program, we’ll want to accomplish the following:
The entire terrain program is listed at the end of this chapter, just before the Reference Section. A copy of the program is in the CH12 source directory on your CD-ROM. Double-click on the TEXSCENE.EXE program icon to try it out!
To keep things simple, we’ll define our terrain as a grid of elevation points with a texture attribute such as “this is water” or “this is a mountain.” Each point in the grid will also have an associated lighting normal to add realism.
#define TERRAIN_SIZE 21 int TerrainType[TERRAIN_SIZE][TERRAIN_SIZE]; GLfloat TerrainHeight[TERRAIN_SIZE][TERRAIN_SIZE]; GLfloat TerrainNormal[TERRAIN_SIZE][TERRAIN_SIZE];
Here the TerrainType array contains the type of terrain at each point and is assigned one of the following control IDs from our user-interface resource file: