Home Textures Loading textures
Loading Textures

This tutorial will show you how to use textures (without my special code in GLM) so you could play with other loaders.

First of all it is important to understand that putting the texture on the model (mapping it to tell OpenGL which part of the image goes where) has to be done explicitly. That is when you export the model from your favorite modeling tool texture mapping coordinates need to be exported too along with the model geometry. 3D Studio max will do this automaticly and most loaders (like GLM) know how to use texture coordinate information. The only thing left to do is to tell which image goes on what object. 3D Studio Max creates a material library file that associates objects with texture files, but it's still your duty to call the OpenGL loading functions and select the right texture before drawing a file. Some loaders read the MTL files and do this for you. The original GLM from the glut example folder doesn't do this! The version modified by me does. This was the whole point of it :) Other loaders online may do the same. In this tutorial however I'm showing you how to do it manually, so please use the original GLM that only knows how to draw texture coordinates but doesn't also load the images or associate them with objects!

So lets start with making the model.

We want to make a cube and apply a different image on each face of the cube. For this create a new TGA image (Tga because it's the most used texture type in games - if you have a bmp or jpeg loader by all means use it). You can use Irfan View for this. It's freeware so google it.

Make your image look something like this:

texture example

I know it's childish :D That is not the point. What matters is that they look different. Each of the 4 small images in the texture should go on a different face of the cube. Looking at it from OpenGL we will know we got the mapping right if it looks ok.

So make a cube in 3D Studio Max or Gmax (and make it an editable poly) and drag the texture on it (drag it from explorer directly on you model). The texture will imediatly appear on it but will most probably not be positioned right. Add the Unwrap UVW modifier. This modifier gives you complete control on the texture mapping (sometimes more then you want - but now since we have a very simple model use it). Now in the modifier's properties click "Edit...". A new window will appear that should show the texture and the edges that define your model over it. If you see a checker patter instead on your texture there is a drop down box in the upper right corner of the window that sais Checker Pattern (checker). Click on it and select your texture from the drop down list.

You have 6 squares on drawn with green on the screen each on top of the other. Start dragging the points of the green edges. Make that square fit over one of the 4 parts of the texture image. Do the same with the others. While dragging the points you can also take a look at the model (that is if your texture edditing window is not maximized) to see how the textures on it are chaging.

 

 

 

In the end you want to have something like this:

UVW Texture map

When you are done export the model in an OBJ file (this time don't use material libraries - we will do it by hand). If you are useing Gmax instead of 3D Studio Max you woun't have a choice since Gmax doesn't support material libraries anyway.

You should end up just with the .OBJ file and the .TGA texture. Now the obj file will also have texture coordinates in it along with the triangles coordinates.

To load the texture in OpenGL you need to go through the following steps:

  1. Open the image (bmp/jpeg/tga) and extract the data from it - normally you would get an image loader for this...no need to write one since there are plenty free on the internet. You can get a TGA loader here
  2. Call glGenTextures to create a new empty texture and return it's index that you can later use to reference to it
  3. Make the new texture the current active 2D texture by calling glBindTexture
  4. Use glTexImage2D to load the image data into the current OpenGL texture (it will be loaded into the video card's memory)
  5. Apply all the needed properties for the texture with glTexParameteri
  6. Don't forget to enable textures by calling glEnable(GL_TEXTURE_2D);

Whenever you want to draw something that should be textured you must bind the texture first (bind the texture's id).

How does OpenGL know where to display what part of the texture? When you export the model from 3D studio max it should also export texture coordinates. These coordinates are numbers between 0 and 1 that tell how to map the texture on each poligon you draw. When you draw your model it's not enought to use glVertex for the poligons but to define textures you need to use glTexCoord2fv too. It's simple as that. Ofcourse GLM and any respectable model loader should do this for you.

 

Download the code for this example here

#include <stdafx.h>
#include <stdlib.h>
#include <windows.h>
#include <GL/glut.h>
#include "glm.h"
#include "Texture.h"

static float ypoz = 0, zpoz = 0;

GLMmodel* pmodel2 = NULL;

// You need an integer value to store the texture index that OpenGL retures when you load a texture
// Using this index you can later refer to a specific texture
// Note that GLuint is actualy an int.

GLuint texture;
Texture treeTexture;

bool LoadTreeTextures()
{

// first of all we call the tga file loader. It doesn't do anything special: it fills the Texture struct with information about
// the image (height, width, bits per pixel). You can easily replace it with a function to load bmps or jpegs.
// The important thing is do load the image corectly in the structure you give it
if (LoadTGA(&treeTexture, "fete_cub.tga"))
{
// This tells opengl to create 1 texture and put it's ID in the given integer variable
// OpenGL keeps a track of loaded textures by numbering them: the first one you load is 1, second is 2, ...and so on.
glGenTextures(1, &treeTexture.texID);
// Binding the texture to GL_TEXTURE_2D is like telling OpenGL that the texture with this ID is now the current 2D texture in use
// If you draw anything the used texture will be the last binded texture
glBindTexture(GL_TEXTURE_2D, treeTexture.texID);
// This call will actualy load the image data into OpenGL and your video card's memory. The texture is allways loaded into the current texture
// you have selected with the last glBindTexture call
// It asks for the width, height, type of image (determins the format of the data you are giveing to it) and the pointer to the actual data
glTexImage2D(GL_TEXTURE_2D, 0, treeTexture.bpp / 8, treeTexture.width, treeTexture.height, 0, treeTexture.type, GL_UNSIGNED_BYTE, treeTexture.imageData);

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glEnable(GL_TEXTURE_2D);
if (treeTexture.imageData)
{
// You can now free the image data that was allocated by LoadTGA
// You don't want to keep a few Mb of worthless data on heap. It's worthless because OpenGL stores the image someware else after
// you call glTexImage2D (usualy in you video card)
free(treeTexture.imageData);
}
return true;
} // Return The Status
else return false;

}

void init(void)
{

glClearColor (0.0, 0.0, 0.0, 0.0);
LoadTreeTextures();
glEnable(GL_DEPTH_TEST);
glShadeModel (GL_SMOOTH);

}


void drawmodel(void)
{

// Load the model just as I have shown you in the previous tutorial
// You could replace this part with your own loader
if (!pmodel2)
{
pmodel2 = glmReadOBJ("cub.obj");
if (!pmodel2) exit(0);
glmUnitize(pmodel2);
glmFacetNormals(pmodel2);
glmVertexNormals(pmodel2, 90.0);
}
// before you draw anything you should bind the right texture
// If you have more then one texture you will have to make sure you bind the right one
glBindTexture(GL_TEXTURE_2D, treeTexture.texID);
// You can replace this with your drawing code
// This calls GLM to draw the loaded model with GLM_TEXTURE on to tell it to also render texture coordinates
// without those coordinates OpenGL will have no ideea how to map your texture to the object
glmDraw(pmodel2, GLM_SMOOTH | GLM_TEXTURE );

}


void display(void)
{

glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity ();

glTranslatef(0,0,-2);
// Lets do some rotations here so you can see the textures on the cube
glRotatef(ypoz,0,1,0);
glRotatef(zpoz,0,0,1);

drawmodel();

Sleep(5);
glutSwapBuffers();



}

void reshape (int w, int h)
{

glViewport (0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
gluPerspective(60.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);
glMatrixMode (GL_MODELVIEW);

}

void keyboard(unsigned char key, int x, int y)
{

switch (key)
{
case 27:
exit(0);
break;
// These will allow you to force some extra rotation by pressing the 'y' and 'z' key.
case 'y':
ypoz=ypoz+5;
if (ypoz>360) ypoz=0;
glutPostRedisplay();
break;
case 'z':
zpoz = zpoz+5;
if (zpoz>360) zpoz=0;
glutPostRedisplay();
break;

}

}

// Animate this a little bit
// Changing the rotation angle of the whole scene from 0 to 360 and then refreshing
// the screen for a nice effect
// You can see the cube and the textures better.

void animate()
{

ypoz+=0.5;
if (ypoz>360) ypoz=0;
glutPostRedisplay();

}

int main(int argc, char** argv)
{

glutInit(&argc, argv);
glutInitDisplayMode (GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
glutInitWindowSize (500, 500);
glutInitWindowPosition (100, 100);
glutCreateWindow (argv[0]);
init ();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutIdleFunc(animate);
glutMainLoop();
return 0;

}

 

 
Copyright © 2012 Tudor Carean. All Rights Reserved.