Ejemplo Práctico

Explicamos a continuación la manera de programar lo anteriormente explicado de forma teórica en OpenGL paso a paso:

  1. Habilitar el mapeado de texturas.

    Esto se hace ejecutando la siguiente instrucción:

    glEnable(GL_TEXTURE_2D);

    Si queremos usar texturas en una dimensión (líneas), cambiaremos el parámetro por GL_TEXTURE_1D. Si las dos están habilitadas, por defecto se usara en 2D.

  2. Especificar qué imagen va a ser utilizada como textura.

    Para ello se utiliza la siguiente función:

    void glTexImage2D( GLenum target, GLint level, GLint components, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels );

    Se explica a continuación el significado de cada parámetro:

    • target: debe valer GL_TEXTURE_2D o GL_TEXTURE_1D
    • level: indica el nivel de detalle de la textura. Habitualmente tiene un valor 0.
    • components: indica el número de componentes del color. Usualmente se usan componentes RGB, y especificaremos 3. Pero también se pueden hacer texturas semitransparentes, con lo que se utiliza un formato RGBA (4 componentes).
    • width: indica el ancho de la imagen de la textura. Debe ser una potencia de 2.
    • height: indica el alto de la imagen de la textura. Debe ser una potencia de 2.
    • border: indica si se utiliza un borde en la textura (1) o no (0). Usualmente es 0.
    • format: indica el formato del valor de cada pixel. Normalmente se utiliza GL_RGB.
    • type: indica el tipo de datos usado para cada componente del valor de un pixel. Puede ser uno de los siguientes valores: GL_UNSIGNED_BYTE, GL_SHORT, GL_BYTE, GL_BITMAP, GL_UNSIGNED_SHORT, GL_UNSIGNED_INT, GL_INT o GL_FLOAT.
    • pixels: es un puntero al mapa de valores de los pixels. Es la imagen en si.

    Ejemplo:

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

  3. Mapear la textura.

    Cuando se está dibujando el objeto, hay que indicar, para cada vértice de este, qué posición de la textura le corresponde. Esto se hace mediante la siguiente función, donde (s,t) indica una posición sobre el mapa de la imagen.
    void glTexCoord2f( GLfloat s, GLfloat t);
    Lo que se hace es indicar la coordenada de la textura antes de indicar el vértice del polígono.

    A continuación vamos a ver dos funciones, donde se dibujan un cuadrado y un triángulo, indicando las posiciones de la textura:


    void Cuadrado(void)
    	{
    		glBegin(GL_QUADS);
    			glTexCoord2f(0.0,1.0);glVertex3f(-1.0,1.0,0.0);
    			glTexCoord2f(1.0,1.0);glVertex3f(1.0,1.0,0.0);
    			glTexCoord2f(1.0,0.0);glVertex3f(1.0, -1.0,0.0);
    			glTexCoord2f(0.0,0.0);glVertex3f(-1.0,-1.0,0.0);
    		glEnd();
    	}
    
    	void Triangulo(void)
    	{
    		glBegin(GL_QUADS);
    			glTexCoord2f(0.0,1.0);glVertex3f(-1.0,1.0,0.0);
    			glTexCoord2f(1.0,0.0);glVertex3f(1.0, -1.0,0.0);
    			glTexCoord2f(0.0,0.0);glVertex3f(-1.0,-1.0,0.0);
    		glEnd();
    }

  4. Indicar cómo la textura va a ser aplicada a cada pixel.

    En este paso es donde se indican diferentes parámetros que van a tener variopintos efectos sobre el resultado final. Entre los más destacables está la repetición de la textura o no, el filtrado y las funciones de texturización que ya se vieron en el apartado tres, pero además hay otros más que son también interesantes como los mipmap, y que se utiliza para evitar problemas visuales sobre la textura cuando el observador se aleja o acerca a la misma.

    Básicamente el manejo de estos parámetros se va a realizar con la función que se indica a continuación en la cual se indicarán los valores correspondientes para dar el efecto deseado:

    void glTexParameterf( GLenum target, GLenum pname, GLfloat param );

  5. Código fuente para textura 2D.

    A continuación vamos a ver un ejemplo que dibuja un cubo que va rotando por la pantalla y al cual se le ha aplicado una textura. La salida será como se muestra debajo:




    #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)       //carga un bitmap
    {
    	FILE *File=NULL;
    	if (!Filename) return NULL;
    	File=fopen(Filename,"r");
    	if (File)
    	{
    		fclose(File);
    		return auxDIBImageLoad(Filename);
    	}
    	return NULL;
    }
    
    int LoadGLTextures()            // Convierte el bitmap a textura
    {
    	int Status=FALSE;
    
    	AUX_RGBImageRec *TextureImage[1];
    	memset(TextureImage,0,sizeof(void *)*1);
    	if (TextureImage[0]=LoadBMP("imagen.bmp"))
    	{
    		Status=TRUE;
    		glGenTextures(1, &texture[0]);
    		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 (TextureImage[0]->data) { free(TextureImage[0]->data); } free(TextureImage[0]); } return 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; }

  6. Código fuente para textura 1D.

    En el ejercicio anterior se mostró el resultado de aplicar una textura con un determinado número de pixels de alto y ancho. En este ejemplo se va a mostrar como el uso de texturas 1D también proporciona imágenes de calidad.

    Se muestra la salida que produce el siguiente código de ejemplo. Se pueden cambiar parámetros de aplicación de la textura con las teclas "e", "o", "s", "x" y "t".



    #include <math.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <GL/glut.h>
    
    #ifdef _WIN32
    #define floorf(x) ((float)floor((x)))
    #endif
    
    static float transx = 1.0, transy, rotx, roty;
    static int ox = -1, oy = -1;
    static int mot = 0;
    #define PAN 1
    #define ROT 2
    
    void pan(const int x, const int y) {
    	transx += (x -ox)/5.;
    	transy -= (y-oy)/5.;
    	ox = x; oy = y;
    	glutPostRedisplay();
    }
    
    void rotate(const int x, const int y) {
    	rotx += x-ox;
    	if (rotx > 360.) rotx -= 360.;
    	else if (rotx < -360.) rotx += 360.;
    	roty += y-oy;
    	if (roty > 360.) roty -= 360.;
    	else if (roty < -360.) roty += 360.;
    	ox = x; oy = y;
    	glutPostRedisplay();
    }
    
    void motion(int x, int y) {
    	if (mot == PAN) pan(x, y);
    	else if (mot == ROT) rotate(x,y);
    }
    
    void mouse(int button, int state, int x, int y) {
    	if(state == GLUT_DOWN) {
    		switch(button) {
    		case GLUT_LEFT_BUTTON:
    			mot = PAN;
    			motion(ox = x, oy = y);
    			break;
    		case GLUT_MIDDLE_BUTTON:
    			mot = ROT;
    			motion(ox = x, oy = y);
    			break;
    		case GLUT_RIGHT_BUTTON:
    			break;
    		}
    	} else if (state == GLUT_UP) mot = 0;
    }
    
    #define stripeImageWidth 32
    GLubyte stripeImage[4*stripeImageWidth];
    
    void makeStripeImage(void) {
    int j;
    
    for (j = 0; j < stripeImageWidth; j++) {
    	stripeImage[4*j] = (GLubyte) ((j<=4) ? 255 : 0);
    	stripeImage[4*j+1] = (GLubyte) ((j>4) ? 255 : 0);
    	stripeImage[4*j+2] = (GLubyte) 0;
    	stripeImage[4*j+3] = (GLubyte) 255;
    	}
    }
    
    void hsv_to_rgb(float h,float s,float v,float *r,float *g,float *b)
    {
    	int i;
    	float f, p, q, t;
    	
    	h *= 360.0;
    	if (s==0) {
    		*r = v;
    		*g = v;
    		*b = v;
    	} else {
    		if (h==360)
    			h = 0;
    		h /= 60;
    		i = floorf(h);
    		f = h - i;
    		p = v*(1.0-s);
    		q = v*(1.0-(s*f));
    		t = v*(1.0-(s*(1.0-f)));
    		switch (i) {
    			case 0 :
    				*r = v;
    				*g = t;
    				*b = p;
    				break;
    			case 1 :
    				*r = q;
    				*g = v;
    				*b = p;
    				break;
    			case 2 :
    				*r = p;
    				*g = v;
    				*b = t;
    				break;
    			case 3 :
    				*r = p;
    				*g = q;
    				*b = v;
    				break;
    			case 4 :
    				*r = t;
    				*g = p;
    				*b = v;
    				break;
    			case 5 :
    				*r = v;
    				*g = p;
    				*b = q;
    				break;
    		}
    	}
    }
    
    GLubyte rainbow[4*stripeImageWidth];
    void makeRainbow(void) {
    	int j;
    	for (j = 0; j < stripeImageWidth; j++) {
    		float r, g, b;
    		hsv_to_rgb((float)j/(stripeImageWidth-1.f), 1.0, 1.0, &r, &g, &b);
    		rainbow[4*j] = r*255;
    		rainbow[4*j+1] = g*255;
    		rainbow[4*j+2] = b*255;
    		rainbow[4*j+3] = (GLubyte) 255;
    	}
    }
    
    /* planes for texture coordinate generation */
    static GLfloat xequalzero[] = {1.0, 0.0, 0.0, 0.0};
    static GLfloat slanted[] = {1.0, 1.0, 1.0, 0.0};
    static GLfloat *currentCoeff;
    static GLenum currentPlane;
    static GLint currentGenMode;
    
    void init(void) {
    	glClearColor (0.0, 0.0, 0. 0, 0.0);
    	glEnable(GL_DEPTH_TEST);
    	glShadeModel(GL_SMOOTH);
    	
    	makeStripeImage();
    	makeRainbow();
    	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    	
    	glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    	glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    	glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    	glTexImage1D
    (GL_TEXTURE_1D, 0, 4, stripeImageWidth, 0, GL_RGBA, GL_UNSIGNED_BYTE, stripeImage); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); currentCoeff = xequalzero; currentGenMode = GL_OBJECT_LINEAR; currentPlane = GL_OBJECT_PLANE; glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, currentGenMode); glTexGenfv(GL_S, currentPlane, currentCoeff); glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_1D); glEnable(GL_CULL_FACE); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_AUTO_NORMAL); glEnable(GL_NORMALIZE); glFrontFace(GL_CW); glCullFace(GL_BACK); glMaterialf (GL_FRONT, GL_SHININESS, 64.0); } void tfunc(void) { static state; if (state ^= 1) { glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage1D
    (GL_TEXTURE_1D, 0, 4, stripeImageWidth, 0, GL_RGBA, GL_UNSIGNED_BYTE, rainbow); } else { glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage1D
    (GL_TEXTURE_1D, 0, 4, stripeImageWidth, 0, GL_RGBA, GL_UNSIGNED_BYTE, stripeImage); } glutPostRedisplay(); } void display(void) { static GLUquadric *q; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); glTranslatef(0., 0., transx); glRotatef(rotx, 1.0, 0.0, 0.0); glRotatef(45.0, 0.0, 0.0, 1.0); glutSolidTeapot(2.0); #if 0 if (!q) q = gluNewQuadric(); gluQuadricTexture(q, GL_TRUE); gluCylinder(q, 1.0, 2.0, 3.0, 10, 10); #endif glPopMatrix(); glutSwapBuffers(); } void reshape(int w, int h) { glViewport(0, 0, (GLsizei) w, (GLsizei) h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (w <= h) glOrtho
    (-3.5, 3.5, -3.5*(GLfloat)h/(GLfloat)w, 3.5*(GLfloat)h/(GLfloat)w, -3.5, 3.5); else glOrtho
    (-3.5*(GLfloat)w/(GLfloat)h,3.5*(GLfloat)w/(GLfloat)h, -3.5, 3.5, -3.5, 3.5); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } /*ARGSUSED1*/ void keyboard (unsigned char key, int x, int y) { switch (key) { case 'e': case 'E': currentGenMode = GL_EYE_LINEAR; currentPlane = GL_EYE_PLANE; glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, currentGenMode); glTexGenfv(GL_S, currentPlane, currentCoeff); glutPostRedisplay(); break; case 'o': case 'O': currentGenMode = GL_OBJECT_LINEAR; currentPlane = GL_OBJECT_PLANE; glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, currentGenMode); glTexGenfv(GL_S, currentPlane, currentCoeff); glutPostRedisplay(); break; case 's': case 'S': currentCoeff = slanted; glTexGenfv(GL_S, currentPlane, currentCoeff); glutPostRedisplay(); break; case 'x': case 'X': currentCoeff = xequalzero; glTexGenfv(GL_S, currentPlane, currentCoeff); glutPostRedisplay(); break; case 't': tfunc(); break; case 27: exit(0); break; default: break; } } int main(int argc, char*argv[]) { glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(512, 512); glutInitWindowPosition(100, 100); glutInit(&argc, argv); glutCreateWindow(argv[0]); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutKeyboardFunc(keyboard); glutMouseFunc(mouse); glutMotionFunc(motion); glutMainLoop(); return 0; }


Trabajo realizado para la asignatura de Gráficos de Computación de la UDC, del curso 2008/2009, por los alumnos
Jorge Abad Casares, Rafael Bermudez Míguez, Yerai Doval Mosquera y Jorge Pérez Rodríguez