OpenGL:Tutorials:Tutorial Framework:Texture Mapping

From GPWiki
Jump to: navigation, search

Texture mapping is the application of an image to a polygon, this gives the illusion of detail without pushing up the poly count.


Loading the Texture

For this tutorial I have used our own TGA loader as the image loader. The code was simply cut and pasted into 'tga.h'.
You can use any method to load an image as long as you know five things:

  • A pointer to the image data,
  • The image width,
  • The image height,
  • The color depth,
  • The format of the image data,

I'm going to bundle up the texture loading into LoadTexture(), this function takes and filename and returns a GLuint texture handle.


OpenGL uses it's own GLuint (unsigned integer) datatype as a handle to reference textures once they are loaded. In this example we have two texture handles:

  GLuint TexID1,TexID2;      // Handles to our textures

This could easily be an array or a struct of your own definition.


The texture handle must be initalised before use:

   glGenTextures(1,&Texture);            // Allocate space for texture

glGenTextures() generates the texture handle in the second parameter, you can generate multiple handles by specifying the quantity in the first parameter and an array in the second.


Once we have our handle, we can bind to it:

   glBindTexture(GL_TEXTURE_2D,Texture); // Set our Tex handle as current

This means that all subsequent texture operations will use the specified texture.


Now we pass our image to OpenGL to produce the texture:

   glTexImage2D(GL_TEXTURE_2D,0,3,Img.GetWidth(),Img.GetHeight(),0,GL_RGB,GL_UNSIGNED_BYTE,Img.GetImg());

The TGA image class is providing most of the information here, the width, height, img pointer as discussed above.
This call is for a 24-bit texture, the 3rd ,7th and 8th parameters specify the image format. See the source for 32-bit image parameters. The second parameter is the mip map level, 0 will do for now, I'll cover mip maps another time.


Now our image has been passed to OpenGL, we can discard our copy, OpenGL will manage the image until we call glDeleteTextures() to remove it. However, there are a few more things that we must do before we can use the texture:

   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);

glTexParameter() is another function that comes in a few flavours. Some versions accept float values or arrays as arguments. Here we just need to specify a filter to use on our texture when it is scaled. The most common filters are GL_NEAREST and GL_LINEAR. GL_LINEAR produces the best results, but GL_NEAREST is faster. We also specify texture wrapping behaviour here, GL_REPEAT means that texture coordinates outside the range 0.0 - 1.0 will produce duplicates of the texture, while GL_CLAMP stops any wrapping from occuring.

Now our texture is ready for use.


Applying the Texture

Applying the texture to a polygon is achieved by specifying texture coordinates alongside the vertices.

First we have to enable texture mapping:

    glEnable(GL_TEXTURE_2D);


We also have to tell OpenGL which texture to use:

   glBindTexture(GL_TEXTURE_2D,TexID1);


The command glTexCoord() is used to specify the texture coordinates. It's another multi-flavour GL function, here we're using the float version:

   glBegin(GL_QUADS);
    glTexCoord2f(1.0f,1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
    glTexCoord2f(1.0f,0.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
    glTexCoord2f(0.0f,0.0f); glVertex3f( 1.0f, 1.0f,-1.0f);
    glTexCoord2f(0.0f,1.0f); glVertex3f(-1.0f, 1.0f,-1.0f);
   glEnd();

Again, the range of the texture coords is 0.0 - 1.0. This seems weird when the obvious measure for image maps is pixels. However, this system means that we can change the texture size and still use the same mapping coords to produce the same result. An easy way to calculate these coords is (Pixel X Pos / Image Width) and (Pixel Y Pos / Image Height). Remember that the behaviour or texture coords outside the range 0.0 - 1.0 will be defined by the TexParameter settings.


Here's how the mapping looks:
GLTut3TexCube.jpg

Press 't' during the demo to see the texture mapped using partial sections around the side faces of the cube.


Finally, we delete the textures to clean up:

   glDeleteTextures(1,&TexID1);
   glDeleteTextures(1,&TexID2);


Source Code

The source to Render.cpp, compile this demo using the OpenGL Tutorial Framework.

#include "Framework.h"
#include "tga.h"
 
GLuint LoadTexture(char *TexName);
 
void Render(void)
 {
  GLuint TexID1,TexID2;      // Handles to our textures
  float Rotate=0.0f;         // A rotation value to be used to spin our polygon
 
  glClearColor(0,0,0,0);
 
  // Setup our screen
  glViewport(0,0,800,600);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(45.0f,800.0f/600.0f,1.0f,500.0f);
  glMatrixMode(GL_MODELVIEW);
 
  // Ensure correct display of polygons
  glEnable(GL_CULL_FACE);
  glEnable(GL_DEPTH_TEST);
  glDepthMask(GL_TRUE);
 
  // Load the textures
  TexID1=LoadTexture("Logo.tga");
  TexID2=LoadTexture("Logo2.tga");
 
  // This loop will run until Esc is pressed
   while(RunLevel)
    {
      if(Keys[VK_ESCAPE]) // Esc Key
       RunLevel=0;
 
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
     glLoadIdentity(); // Reset current matrix (Modelview)
 
     // Do our rotations
     glTranslatef(0.0f,0.0f,-5.0f);
     glRotatef(Rotate,0.0f,0.0f,1.0f);
     glRotatef(Rotate,1.0f,0.6f,0.0f);
 
     // Enable texturing and select first texture
     glColor3f(1.0f,1.0f,1.0f);
     glEnable(GL_TEXTURE_2D);
     glBindTexture(GL_TEXTURE_2D,TexID1);
 
     // Draw the top face
     glBegin(GL_QUADS);
      glTexCoord2f(1.0f,1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
      glTexCoord2f(1.0f,0.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
      glTexCoord2f(0.0f,0.0f); glVertex3f( 1.0f, 1.0f,-1.0f);
      glTexCoord2f(0.0f,1.0f); glVertex3f(-1.0f, 1.0f,-1.0f);
     glEnd();
 
     // Select second texture
     glBindTexture(GL_TEXTURE_2D,TexID2);
 
     glBegin(GL_QUADS);
      glTexCoord2f(1.0f,0.0f); glVertex3f(-1.0f,-1.0f, 1.0f);
      glTexCoord2f(0.0f,0.0f); glVertex3f(-1.0f,-1.0f,-1.0f);
      glTexCoord2f(0.0f,1.0f); glVertex3f( 1.0f,-1.0f,-1.0f);
      glTexCoord2f(1.0f,1.0f); glVertex3f( 1.0f,-1.0f, 1.0f);
     glEnd();
 
     // Press 't' to texture the side faces
      if(Keys['T'])
       {
        glEnable(GL_TEXTURE_2D); // Draw textured side faces
        glBegin(GL_TRIANGLE_STRIP);
         glTexCoord2f(1.0f,0.0f);  glVertex3f(-1.0f, 1.0f, 1.0f);
         glTexCoord2f(0.0f,0.0f);  glVertex3f(-1.0f,-1.0f, 1.0f);
         glTexCoord2f(1.0f,0.3f);  glVertex3f( 1.0f, 1.0f, 1.0f);
 
         glTexCoord2f(0.0f,0.3f);  glVertex3f( 1.0f,-1.0f, 1.0f);
 
         glTexCoord2f(1.0f,0.5f);  glVertex3f( 1.0f, 1.0f,-1.0f);
         glTexCoord2f(0.0f,0.5f);  glVertex3f( 1.0f,-1.0f,-1.0f);
 
         glTexCoord2f(1.0f,0.75f); glVertex3f(-1.0f, 1.0f,-1.0f);
         glTexCoord2f(0.0f,0.75f); glVertex3f(-1.0f,-1.0f,-1.0f);
 
         glTexCoord2f(1.0f,0.95f); glVertex3f(-1.0f, 1.0f, 1.0f);
         glTexCoord2f(0.0f,0.95f); glVertex3f(-1.0f,-1.0f, 1.0f);
        glEnd();
       }
      else
       {
        glDisable(GL_TEXTURE_2D); // Draw colored side faces
        glBegin(GL_TRIANGLE_STRIP);
         glColor3f(0.8f,0.8f,0.8f); // Gray
         glVertex3f(-1.0f, 1.0f, 1.0f);
         glVertex3f(-1.0f,-1.0f, 1.0f);
         glVertex3f( 1.0f, 1.0f, 1.0f);
         glColor3f(0.0f,1.0f,0.0f); // Green
         glVertex3f( 1.0f,-1.0f, 1.0f);
         glColor3f(1.0f,1.0f,0.0f); // Yellow
         glVertex3f( 1.0f, 1.0f,-1.0f);
         glColor3f(0.0f,1.0f,1.0f); // Aqua?
         glVertex3f( 1.0f,-1.0f,-1.0f);
         glColor3f(0.6f,0.6f,0.6f); // Gray
         glVertex3f(-1.0f, 1.0f,-1.0f);
         glColor3f(0.1f,0.1f,0.1f); // Dark Gray
         glVertex3f(-1.0f,-1.0f,-1.0f);
         glColor3f(0.0f,0.0f,1.0f); // Blue
         glVertex3f(-1.0f, 1.0f, 1.0f);
         glColor3f(1.0f,0.0f,1.0f); // Red
         glVertex3f(-1.0f,-1.0f, 1.0f);
        glEnd();
       }
 
     // Add to the rotation for next frame 
     Rotate+=0.05f;
 
     // Show our cube
     FlipBuffers();
    }
 
 
  // Clean up
  glDeleteTextures(1,&TexID1);
  glDeleteTextures(1,&TexID2);
 }
 
 
 
 GLuint LoadTexture(char *TexName)
  {
   TGAImg Img;        // Image loader
   GLuint Texture;
 
  // Load our Texture
   if(Img.Load(TexName)!=IMG_OK)
    return -1;
 
   glGenTextures(1,&Texture);            // Allocate space for texture
   glBindTexture(GL_TEXTURE_2D,Texture); // Set our Tex handle as current
 
   // Create the texture
    if(Img.GetBPP()==24)
     glTexImage2D(GL_TEXTURE_2D,0,3,Img.GetWidth(),Img.GetHeight(),0,
                  GL_RGB,GL_UNSIGNED_BYTE,Img.GetImg());
    else if(Img.GetBPP()==32)
     glTexImage2D(GL_TEXTURE_2D,0,4,Img.GetWidth(),Img.GetHeight(),0,
                  GL_RGBA,GL_UNSIGNED_BYTE,Img.GetImg());
    else
     return -1;
 
   // Specify filtering and edge actions
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
 
   return Texture;
  }

Downloads

OpenGL_Tut3_(TexMap).zip - A zip including all source code, image files and Win32 exe.