Realistic 3D environments, especially in games, are based on illusions. Illusions generated by images. These are the textures applied on surfaces. It is a lot easier, and faster, to use the image of a complicated object, instead of drawing its geometry. Take for example the tyre of a car. The tread is rather complex and drawing it requires a lot of graphics memory to store the geometry and GPU power to process it.
Now suppose we want to draw the earth rotating to show the change between day and night. Can you imagine the amount of data required to represent the earth's surface? If we apply a good image of the earth showing the continents and the sea on a simple sphere, we can create the illusion of the earth. We can then rotate the sphere any way we want and get a good view of the earth from any point of view we want.
Let's start putting things down and see what we have to do to display the earth as shown in the image.
First we must load the texture image into memory. For this purpose I give you the code I found on the internet that reads TGA image files. I prefer TGA images because they provide Alpha channel which is very useful for more advanced techniques we will cover in the future. In order to load the texture we call the 'clf_loadTexture' function. It is located in the 'clf_resources.cpp'. This function loads the image and then calls OpenGL to load the image data into its buffers. It also instructs OpenGL to generate 'mipmaps'. These are scaled down versions of the original image that are used when the drawing are is small. This technique (handled internally by OpenGL) speeds up drawing and improves image quality. Finally the original image is released since it is no longer required. 'clf_loadTexture' returns the texture identifier created by OpenGL.
GLuint clf_loadTexture(const char* fname) { GLuint tex = -1; // default return is failure clf_targa_image image; // try to load the TGA image if (!image.load(fname)) { return tex; // return failure (invalid OpenGL id) } glPixelStorei (GL_UNPACK_ALIGNMENT, 1); // how bytes are aligned in memory glEnable(GL_TEXTURE_2D); // enable textures for the following commands // we will generate a texture with mipmaps glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); glGenTextures(1, &tex); // generate texture glBindTexture(GL_TEXTURE_2D, tex); // and make it the current processing object glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); // how texture values (colours) are interpreted // when texture area is small, bilinear filter the closest mipmap glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST ); // when texture area is large, bilinear filter the original glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); // the texture wraps over at the edges (repeat) glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); // now build the mipmaps gluBuild2DMipmaps(GL_TEXTURE_2D, 4, image.get_width(), image.get_height(), image.get_image_format(), image.get_data_type(), image.get_image()); image.release();// release the TGA image return tex; // return valid texture id }
Now all we have to do is enable the use of textures, point to the texture we want to use, and provide the texture coordinates for each vertex of our geometry. The first two steps are straight forward and we don't have much to talk about. First we call
glEnable(GL_TEXTURE_2D);
to turn the texture use on and then
glBindTexture(GL_TEXTURE_2D, texture_id);
to tell which texture to use.
The third step needs some explaining. Here we map each vertex of our geometry i.e. quad, with a point in our image. This makes OpenGL stretch the corresponding image part and fit it on our quad. The following image shows this mapping.
The mapping instruction is 'glTexCoord2f' and it is called before setting the coordinates for the vertex like this
glTexCoord2f(0, 0); glVertex3f(x, y, z);
This code fragment maps the bottom left corner of the image to the vertex whose coordinates we give afterwards.
The example program we developed for this article will be the basis for many more examples and the code in the 'gog_core' group of files can be used as a general purpose game programming infrastructure.