Learning how to texture map has many benefits. Lets say you wanted a missle to fly across the screen. Up until this tutorial we'd probably make the entire missle out of polygons, and fancy colors. With texture mapping, you can take a real picture of a missle and make the picture fly across the screen. Which do you think will look better? A photograph or an object made up of triangles and squares? By using texture mapping, not only will it look better, but your program will run faster. The texture mapped missle would only be one quad moving across the screen. A missle made out of polygons could be made up of hundreds or thousands of polygons. The single texture mapped quad will use alot less processing power.

Lets start off by adding four new lines of code to the top of lesson one. The first three lines are floating point variables... xrot, yrot and zrot. These variables will be used to rotate the cube on the x axis, the y axis, and the z axis. The fourth line GLuint texture[1] sets aside storage space for one texture. If you wanted to load in more than one texture, you would change the number one to the number of textures you wish to load.

#include <windows.h>		// Header File For Windows
#include <gl\gl.h>		// Header File For The OpenGL32 Library
#include <gl\glu.h>		// Header File For The GLu32 Library
#include <gl\glaux.h>		// Header File For The GLaux Library

static	HGLRC hRC;		// Permanent Rendering Context
static	HDC hDC;		// Private GDI Device Context

BOOL	keys[256];		// Array Used For The Keyboard Routine

GLfloat	xrot,			// X Rotation ( NEW )
GLfloat	yrot,			// Y Rotation ( NEW )
GLfloat	zrot;			// Z Rotation ( NEW )

GLuint	texture[1];		// Storage For One Texture ( NEW )

Now immediately after the above code, and before InitGL, we want to add the following section of code. The job of this code is to load a bitmap file, and convert it into a texture. Before I start explaining the code there are a few VERY important things you need to know about the images you use as textures. The image height and width MUST be a power of 2. The width and height must be at least 64 pixels, and for compatability reasons, no more than 256 pixels. If the image you want to use is not 64, 128 or 256 pixels on the width and height, resize it in an art program. There are ways around this limit, but for now we'll just stick to standard texture sizes.

AUX_RGBImageRec *texture1 creates a record for the first bitmap we load and use as a texture. The record will hold the red, green and blue data used to create the image. Basically it's a place to store the loaded bitmap. AUX_RGBImageRec is part of the glAux library, and makes loading bitmaps a snap. The next line does the actual loading. The bitmap file called NeHe.bmp in the Data folder will be loaded and stored in the texture1 record we created above with AUX_RGBImageRec.

// Load Bitmaps And Convert To Textures
GLvoid LoadGLTextures()
{
	// Load Texture
	AUX_RGBImageRec *texture1;
	texture1 = auxDIBImageLoad("Data/NeHe.bmp");

Now that we've loaded the image as red, green and blue data, we will build a texture using this data. The first line glGenTextures(1, &texture[0]) tells OpenGL we want to build one texture (increase the number if you load more than one texture), and we want the texture we build to be stored in slot 0 of texture[]. Remember at the very beginning of this tutorial we created room for one texture with the line GLuint texture[1]. Although you'd think we should be storing it at &texture[1], it wont work. The first actual storage area is 0. If we wanted two textures we would use GLuint texture[2] and the second texture would be stored at texture[1].

The second line glBindTexture(GL_TEXTURE_2D, texture[0]) tells OpenGL that texture[0] (the first texture) will be a 2D texture. 2D textures have both height (on the Y axes) and width (on the X axes).

	// Create Texture
	glGenTextures(1, &texture[0]);
	glBindTexture(GL_TEXTURE_2D, texture[0]);

The next two lines tell OpenGL what type of filtering to use when the image is larger (GL_TEXTURE_MAG_FILTER) on the screen than the original texture actually is, or when it's smaller (GL_TEXTURE_MIN_FILTER) on the screen than the actual texture really is. I usually use GL_LINEAR for both. This makes the texture look smooth way in the distance, and when it up close to the screen. Using GL_LINEAR requires alot of work from the processor/video card, so if your system is slow, you might want to use GL_NEAREST. A texture that's filtered with GL_NEAREST appears blocky when it's zoomed on the screen. You can also try a combination of both. Make it filter things up close, and not things in the distance.

	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

Finally we create the actual texture. The following line tells OpenGL the texture will be a 2D texture (GL_TEXTURE_2D). Zero represents the images level of detail, this is usually left at zero. Three is the number of data components. Because the image is made up of red data, green data and blue data, there are three components. texture1->sizeX gets the width of the texture automatically. If you know the width, you can put it there instead, but it's easy to let the computer figure it out for you. texture1->sizey get the height of the texture. zero is the border. It's usually left at zero. GL_RGB tells OpenGL the image data we are using is made up of red, green and blue data in that order. GL_UNSIGNED_BYTE means the data that makes up the image is made up of unsigned bytes, and finally... texture1->data tells OpenGL where to get the data from. In this case it points to the data stored in the texture1 record.

	glTexImage2D(GL_TEXTURE_2D, 0, 3, texture1->sizeX, texture1->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, texture1->data);
};

I've added three lines of code to InitGL. I'll repost the entire section of code, so it's easy to see the lines that I've added, and where they go in the code.

The first line LoadGLTextures() jumps to the routine above which loads the bitmap and makes a texture from it. The second line glEnable(GL_TEXTURE_2D) enables texture mapping. If you don't enable texture mapping your object will usually appear solid white, which is definitely not good. Finally I've changed the background clear color from black to blue, mainly to show you that the color doesn't have to be black {grin}.

GLvoid InitGL(GLsizei Width, GLsizei Height)
{
	LoadGLTextures();				// Load The Texture(s) ( NEW )
	glEnable(GL_TEXTURE_2D);			// Enable Texture Mapping ( NEW )

	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
	glClearDepth(1.0);
	glDepthFunc(GL_LESS);
	glEnable(GL_DEPTH_TEST);
	glShadeModel(GL_SMOOTH);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f);

	glMatrixMode(GL_MODELVIEW);
}

Now we draw the textured cube. You can replace the DrawGLScene code with the code below, or you can add the new code to the original lesson one code. This section will be heavily commented so it's easy to understand. The first two lines of code glClear() and glLoadIdentity() are in the original lesson one code. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) will clear the screen to the color we selected in InitGL(). In this case, the screen will be cleared to blue. The depth buffer will also be cleared. The view will then be reset with glLoadIdentity().

GLvoid DrawGLScene(GLvoid)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	glTranslatef(0.0f,0.0f,-5.0f);

The following three lines of code will rotate the cube on the x axis, then the y axis, and finally the z axis. How much it rotates on each axis will depend on the value stored in xrot, yrot and zrot.

	glRotatef(xrot,1.0f,0.0f,0.0f);				// Rotate On The X Axis
	glRotatef(yrot,0.0f,1.0f,0.0f);				// Rotate On The Y Axis
	glRotatef(zrot,0.0f,0.0f,1.0f);				// Rotate On The Z Axis

The next line of code selects which texture we want to use while texture mapping. If you had more than one texture you wanted to use in your scene, you would select the texture using glBindTexture(GL_TEXTURE_2D, texture[number of texture to use]) you would then draw some quads using that texture. Every time you wanted to change textures, you would bind to the new texture.

	glBindTexture(GL_TEXTURE_2D, texture[0]);

To properly map a texture onto a quad, you have to make sure the top right of the texture is mapped to the top right of the quad. The top left of the texture is mapped to the top left of the quad, the bottom right of the texture is mapped to the bottom right of the quad, and finally, the bottom left of the texture is mapped to the bottom left of the quad. If the corners of the texture do not match the same corners of the quad, the image may appear upside down, sideways, or not at all.

The first value of glTexCoord2f is the X coordinate. 0.0f is the left side of the texture. 0.5f is the middle of the texture, and 1.0f is the right side of the texture. The second value of glTexCoord2f is the Y coordinate. 0.0f is the bottom of the texture. 0.5f is the middle of the texture, and 1.0f is the top of the texture.

So now we know the top left coordinate of a texture is 0.0f on X and 1.0f on Y, and the top left vertex of a quad is -1.0f on X, and 1.0f on Y. Now all you have to do is match the other three texture coordinates up with the remaining three corners of the quad.

Try playing around with the x and y values of glTexCoord2f. Changing 1.0f to 0.5f will only draw the left half of a texture from 0.0f (left) to 0.5f (middle of the texture). Changing 0.0f to 0.5f will only draw the right half of a texture from 0.5f (middle) to 1.0f (right).

	glBegin(GL_QUADS);
		// Front Face
		glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);	// Bottom Left Of The Texture and Quad
		glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);	// Bottom Right Of The Texture and Quad
		glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);	// Top Right Of The Texture and Quad
		glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);	// Top Left Of The Texture and Quad
		// Back Face
		glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);	// Bottom Right Of The Texture and Quad
		glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);	// Top Right Of The Texture and Quad
		glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);	// Top Left Of The Texture and Quad
		glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);	// Bottom Left Of The Texture and Quad
		// Top Face
		glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);	// Top Left Of The Texture and Quad
		glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,  1.0f,  1.0f);	// Bottom Left Of The Texture and Quad
		glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,  1.0f,  1.0f);	// Bottom Right Of The Texture and Quad
		glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);	// Top Right Of The Texture and Quad
		// Bottom Face
		glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);	// Top Right Of The Texture and Quad
		glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);	// Top Left Of The Texture and Quad
		glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);	// Bottom Left Of The Texture and Quad
		glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);	// Bottom Right Of The Texture and Quad
		// Right face
		glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);	// Bottom Right Of The Texture and Quad
		glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);	// Top Right Of The Texture and Quad
		glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);	// Top Left Of The Texture and Quad
		glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);	// Bottom Left Of The Texture and Quad
		// Left Face
		glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);	// Bottom Left Of The Texture and Quad
		glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);	// Bottom Right Of The Texture and Quad
		glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);	// Top Right Of The Texture and Quad
		glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);	// Top Left Of The Texture and Quad
	glEnd();

Now we increase the value of xrot, yrot and zrot. Try changing the number each variable increases by to make the cube spin faster or slower, or try changing a + to a - to make the cube spin the other direction.

	xrot+=0.3f;			// X Axis Rotation
	yrot+=0.2f;			// Y Axis Rotation
	zrot+=0.4f;			// Z Axis Rotation
}

You should now have a better understanding of texture mapping. You should be able to texture map the surface of any quad with an image of your choice. Once you feel confident with your understanding of 2D texture mapping, try adding six different textures to the cube.

Texture mapping isn't to difficult to understand once you understand texture coordinates. If you're having problems understanding any part of this tutorial, let me know. Either I'll rewrite that section of the tutorial, or I'll reply back to you in email. Have fun creating texture mapped scenes of your own :)

Jeff Molofee (NeHe)

* DOWNLOAD Visual C++ Code For This Lesson.
* DOWNLOAD Delphi Code For This Lesson. ( Conversion by Brad Choate )
* DOWNLOAD Linux Code For This Lesson. ( Conversion by Richard Campbell )
* DOWNLOAD BeOS Code For This Lesson. ( Conversion by Chris Herborth )