Principal | Gráficos 3D | Gráficos 2D | Fractales | Math | Códigos | Tutoriales | Links

Mapeado de Texturas

La utilización de texturas es determinante para dar una apariencia real al material del que estén constituidos los modelos de la escena. Una textura es una imagen que se pega a un modelo tridimensional de forma que parezca que forma parte del objeto.

En OpenGL, si queremos aplicar textura, tenemos que seguir los siguientes pasos:

Activar el mapeado de texturas

Esto se hace ejecutando la siguiente instruccion:
glEnable(GL_TEXTURE_2D)

Especificar que imagen va a ser utilizada como textura

Para ello se utiliza la siguiente funcion:
void glTexImage2D( GLenum target, GLint level, GLint components, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels )

donde:

Ejemplo :
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data)

La imagen se puede obtener de dos formas. Una, generandola con codigo del propio programa. Esto es facil si la textura es sencilla, como puede ser un tablero de ajedrez. Si la imagen es mas complicada, hay que cargarla de un archivo. En el ejemplo que damos aqui, se muestra una función para cargar una textura a partir de un archivo BMP de Windows.
OpenGL exige que las dimensiones de la imagen en unidades de pixel (ancho y alto) , sean 2n x 2m. donde n y m son eneteros.

Mapear la textura

Cuando se esta dibujando el objeto, hay que indicar, para cada vertice de este, que posición de la textura le corresponde. Esto se hace mediante la siguiente función :
void glTexCoord2f( GLfloat s, GLfloat t)
Donde (s,t) indica una posicíon sobre el mapa de la imagen.Lo que se hace es indicar la coordenada de la textura antes de indicar el vertice del polígono. A continuación vemos como se dibuja el poligono del frente, indicando las posicion de la textura :

// Frente
glNormal3f( 0.0f, 0.0f, 1.0f)
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f)
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f)
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f)
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f)

Indicar como la textura va a ser aplicada a cada pixel

Aqui hay varios puntos que indicar. El primero de ellos es indicar que ocurre con el tamaño de las texturas. Cuando uno referencia la coordenadas de las texturas, se indican valores entre 0 y 1, que dan los limites de las texturas. Cuando uno referencia un valor mayor que 1 o menor que 0, se esta fuera del mapa de la imagen. ¿Que hacer en estos casos?. Hay dos posibilidades. La primera es repetir los pixels de los bordes de la textura cuando se referencie fuera de ella, lo cual no parece que tenga mucha utilidad. La otra posibilidad es la de repetir la textura. Esto es, en lugar de tener un mapa con solo una imagen, se tiene un mapa donde la imagen de la textura esta repetida infinitas veces, unas contiguas a las otras.

Para indicar si se quiere repetir el borde de la textura, o se quiere repetir la textura completa se utiliza la siguiente función :
void glTexParameterf( GLenum target, GLenum pname, GLfloat param )

donde :

Ejemplo :
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)

Otro de los parametros a tener en cuenta es el filtrado de las texturas. Cuando la camara esta muy cerca de un objeto con texturas, debido al efecto del mapeado se pueden notar con mucha claridad la diferencia entre los pixels contiguos de la textura, que se ven como unos cuadrados mas grandes cuanto mas cerca se esta del objeto.
Un efecto desagradable aparece tambien cuando se esta lejos de las texturas. Si se tiene objeto lejano con una textura del tipo de un tablero de ajedrez, debido a que solo se dibujan algunos de los pixels de la textura, pueden aparecer formas muy extrañas en la textura.
Si se quiere evitar de forma parcial este efecto, existe la posibilidad de filtrar las texturas. Esto se hace con la siguiente llamada :
void glTexParameterf( GLenum target, GLenum pname, GLfloat param )

donde:

Ejemplo :
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR)

textura.jpg El Cubo 3D, con la textura aplicada.
imagen.jpg Textura utilizada, en este caso es un archivo del tipo bmp (mapa de bits) y de 256x256 pixels.

Código Fuente

Compilación del código fuente

/*
Rotación de un Cubo, con la aplicación de Textura
Autor : Ramiro Alcocer
WWW   : www.oocities.org/valcoey/index.html
Email : valcoey@hotmail.com
*/

#include <GL/glut.h>
#include <gl/gl.h>			
#include <gl/glu.h>	
#include <gl/glaux.h>
#include <stdio.h>		


//angulos de rotacion
GLfloat	xrot=0.0;
GLfloat yrot=0.0;
GLfloat zrot=0.0;

GLint texture[1];		

//parametros de  la fuente de luz
GLfloat LightAmbient[]={ 0.5f, 0.5f, 0.5f, 1.0f };
GLfloat LightDiffuse[]={ 1.0f, 1.0f, 1.0f, 1.0f };
GLfloat LightPosition[]={ 1.0f, 1.0f, 1.0f, 0.0f };	

AUX_RGBImageRec *LoadBMP(char *Filename)	// Loads A Bitmap Image
{
 FILE *File=NULL;				// File Handle
 if (!Filename)					// Make Sure A Filename Was Given
    {
    return NULL;			        // If Not Return NULL
    }
 File=fopen(Filename,"r");			// Check To See If The File Exists
 if (File)					// Does The File Exist?
    {
    fclose(File);				// Close The Handle
    return auxDIBImageLoad(Filename);		// Load The Bitmap And Return A Pointer
    }
 return NULL;					// If Load Failed Return NULL
}

int LoadGLTextures()				// Load Bitmaps And Convert To Textures
{
 int Status=FALSE;				// Status Indicator

 AUX_RGBImageRec *TextureImage[1];		// Create Storage Space For The Texture
 memset(TextureImage,0,sizeof(void *)*1);       // Set The Pointer To NULL
 // Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit
 if (TextureImage[0]=LoadBMP("imagen.bmp"))
    {
    Status=TRUE;					      // Set The Status To TRUE
    glGenTextures(1, &texture[0]);			      // Create The Texture
    // Typical Texture Generation Using Data From The Bitmap
    glBindTexture(GL_TEXTURE_2D, texture[0]);
    glTexImage2D(GL_TEXTURE_2D, 0, 3,
                 TextureImage[0]->sizeX, 
                 TextureImage[0]->sizeY, 
                 0, GL_RGB, GL_UNSIGNED_BYTE, 
                 TextureImage[0]->data);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    }
 if (TextureImage[0])					 // If Texture Exists
    {
    if (TextureImage[0]->data)				 // If Texture Image Exists
       {
       free(TextureImage[0]->data);			 // Free The Texture Image Memory
       }

    free(TextureImage[0]);				 // Free The Image Structure
    }
 return Status;						 // Return The Status
}

void init(void)
{
 //Carga la textura
 LoadGLTextures();							
 glEnable(GL_TEXTURE_2D);							
 glShadeModel(GL_SMOOTH);							
 glClearColor(0.0f, 0.0f, 0.0f, 0.5f);				
 glClearDepth(1.0f);									
 glEnable(GL_DEPTH_TEST);							
 glDepthFunc(GL_LEQUAL);								
 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	

 glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);		
 glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);	
 //posicion de la fuente de luz
 glLightfv(GL_LIGHT1, GL_POSITION,LightPosition);	
 //activa la luz
 glEnable(GL_LIGHT1);	
}

void display(void)
{

 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	
 glLoadIdentity();									
 glTranslatef(0.0f,0.0f,-4.5f);
 //Rota el cubo
 glRotatef(xrot,1.0f,0.0f,0.0f);
 glRotatef(yrot,0.0f,1.0f,0.0f);
 glRotatef(zrot,0.0f,0.0f,1.0f);

 glBindTexture(GL_TEXTURE_2D, texture[0]);
 glBegin(GL_QUADS);
	// Frente
        glNormal3f( 0.0f, 0.0f, 1.0f);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);
	glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);
	glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);
	// parte de Atras
        glNormal3f( 0.0f, 0.0f,-1.0f);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);
	glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);
	glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
	// Arriba
        glNormal3f( 0.0f, 1.0f, 0.0f);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,  1.0f,  1.0f);
	glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,  1.0f,  1.0f);
	glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);
	// Abajo
        glNormal3f( 0.0f,-1.0f, 0.0f);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
	glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
	glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);
	// lado Derecho
        glNormal3f( 1.0f, 0.0f, 0.0f);
	glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
	glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);
	glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);
	glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);
	// Lado Izquierdo
        glNormal3f(-1.0f, 0.0f, 0.0f);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);
 glEnd();
 glFlush ();
 glutSwapBuffers();
}

void reshape (int width, int height)
{
 if (height==0)					
	{
	height=1;				
	}
 glViewport(0,0,width,height);						
 glMatrixMode(GL_PROJECTION);						
 glLoadIdentity();									
 gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
 glMatrixMode(GL_MODELVIEW);							
 glLoadIdentity();	 
}


void keyboard(unsigned char key, int x, int y)
{
 switch (key) 
   {
   case 27: exit(0);
             break;
   }
}                                                                

//Incremento los angulos de rotacion
void Idle(void)
{
 xrot+=5.0f;
 yrot+=6.0f;
 zrot+=7.0f;
 display();
}

int main(int argc, char** argv)
{
   //Inicializar el estado de GLUT 
   glutInit(&argc, argv);

   //Seleccionar el tipo de modo de display Buffer doble y color RGBA 
   glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);

   //Poner el tamaño y posición de la ventana 
   glutInitWindowSize (300, 300); 
   glutInitWindowPosition (0, 0);
   glutCreateWindow ("Textura 3D");

   init ();

   glutDisplayFunc(display); 
   glutReshapeFunc(reshape);
   glutKeyboardFunc(keyboard);
   glutIdleFunc(Idle);
   glutMainLoop();
   return 0;
}


valcoey@hotmail.com

Ramiro Alcocer, 2001

Principal | Gráficos 3D | Gráficos 2D | Fractales | Math | Códigos | Tutoriales | Links