This brief tutorial explains the basics of texture mapping in OpenGL. You can find more information online.
To start, download the zip file here. Unzip the folder, open the visual studio solution, and run the program. You should see two triangles. As you move the camera the color should change. Why is that? (Hint: where is the light positioned?) Next we'll texture map these triangles.
The texture is read in the initializeTexture function. We use a simple procedure to read in bmp files; it only works with 24 bit image (8 bits for each of three channels). If you have a file in another format you can convert it to the prescribed format in gimp, photoshop, etc.
Note: OpenGL requires that texture images have a width and height that are powers of 2! The texture does not have to be square. If you have problems with texture mapping this is the first thing to check.
OpenGL can maintain several textures that you can use during display. Each must have its own integer name. After the comment to create texture in initializeTexture() add the following
// Create texture name glGenTextures(1,&hTexture);The first argument specifies that we want 1 texture to be created. The second is a reference to the integer array where texture names should be stored. In this example we are creating one texture and we provide a reference to the global int that will store its name.
OpenGL supports various types of textures, 1D, 2D, 3D, etc. The most common type is 2D, which is what we will use. To tell OpenGL that hTexture is intended to be a 2D texture, as well as making it the current 2D texture, add the following.
// bind to 2D texture glBindTexture(GL_TEXTURE_2D, hTexture);Next we have to provide the image data that will be used for texturing. Add the following
// supply texture data glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, bitmapInfoHeader.biWidth, bitmapInfoHeader.biHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, bitmapData);
The functions glTexParameterf and glTexParameteri are used to set various texture parameters. Each takes three arguments, the target, the parameter name, and the parameter value. In our example the target will be GL_TEXTURE_2D. There are numerous parameters that can be set and you can read about them online. Here we will only be interested in a few.
The first is the interpolation scheme that is used for re-sampling the image. Let's use bilinear interpolation. Add the following code:
// the following two commands tell openGL how to interpolate texture // values between the sampled values in your texture bitmap glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);OpenGL supports different interpolation schemes for when the texture is minified and magnified. The interpolation schemes we've discussed for previous projects were primarily concerned with magnification. Different types of problems occur with minification. We'll come back to this later.
A second parameter we'll set determines how OpenGL extends our texture to tesselate 2D space. Let's repeat our texture in each dimension. Add the following
// the following two commands tell openGL how to extend the texture when needed glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);Finally, we have to specify how the texture color will be used. The simplest approach is the pixel color. To do this we use the glTexEnvi command, which sets texture environment parameters. Add the following
// use texture as decal glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
In display you need to enable texturing before starting to draw the triangle, provide a texture coordinate fore each vertex during drawing, then disable texturing when done. The texture coordinates are already specified for you. Add
glEnable(GL_TEXTURE_2D);before drawing the triangles and
glDisable(GL_TEXTURE_2D);afterwards.
To understand the problems with minification, change the texture coordinates of the second triangle as follows:
// draw another texture-mapped triangle glBegin(GL_TRIANGLES); glTexCoord2f(0,0); glVertex3f(0,0,0); glTexCoord2f(100,0); glVertex3f(50,0,0); glTexCoord2f(100,100); glVertex3f(0,50,0); glEnd();You'll notice some annoying artifacts of the sampling process on the second triangle, particularly when you move the camera. The problem is that adjacent pixels in the rendered triangle come from distant samples in the texture. You might think this problem could be avoided by providing "good" texture coordinates. Unfortunately it is not so simple. Imagine a ground plane extending off to infinity (or at least the far plane of our frustum) that is textured with grass. In the foreground the texture may be magnified but off in the distance it will be minified. Changing the texture coordinates depending on distance is complicated but it would also mean that the grass would scale in an unnatural way.
This is a common problem in 3D graphics and the solution is minmaps. We can downsample a texture by averaging pixels and use the downsampled version when minification is needed. We're not going to cover minmaps here, but you can read more about it in the red book.
In the previous example you should note that we used texture coordinates outside [0,1] and OpenGL repeated our texture as needed. We could have clamped our texture, which means that texture coordinates outside of [0,1] get clamped. Replace GL_REPEAT by GL_CLAMP and see what happens. Try just changing the GL_TEXTURE_WRAP_S parameter.
In the previous example we used the texture as a decal. There are many other options. To see the difference move the glTexEnvi command into display. Before drawing the first triangle add the line
// use texture as decal glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);After drawing the first triangle and before drawing the second triangle add the following:
// modulate texture glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);Try replacing the GL_DECAL with GL_ADD, GL_BLEND, and GL_REPLACE. Do you understand the various effects? (If not, try googling for answers.)
To further develop your understanding of OpenGL textures, texture map one of the triangles with the texture in texture2.bmp.