OpenGL + Glut

[Principal] [Índice de artículos] [Capítulo 1] [Capítulo 2] [Capítulo 3]

Capítulo 4: Texturas.

Hasta ahora sólo dibujamos y coloreamos, pero ahora le vamos a poner texturas a nuestros programas, algo que es muy útil para crear escenas mas reales y fáciles. No sería muy buena idea dibujar un mundo de pequeños polígonos de diferentes colores, mejor uno grande y le ponemos nuestra textura. Este código es prácticamente una traducción del que existe en la pagina nehe.gamedev.net, capítulo 25, si quieres saber más, este es un sitio genial (en ingles).

Para empezar hay que definir lo que va a guardar a nuestra textura, esto lo hacemos mediante una estructura:

typedef struct {
	GLuyte	*dibujo;	// Un puntero a los datos de la imagen
	GLuint	bpp;		// bpp significa bits per pixel (bits por punto) 
						// es la calidad en palabras sencillas
	GLuint largo;		// Largo de la textura
	GLuint ancho;		// Ancho de la textura
	GLuint ID;		    // ID de la textura, es como su nombre para opengl
} textura;
 
textura	texturas[1];		// Definimos nuestras texturas, por ahora solo 1

Listo, ahora hay que empezar a cargar el código en la memoria. El código que viene a continuación solo carga tga de 24 o 32 bits sin compresión. cabezeraTGA va a guardar 12 bytes que describen un archivo tga, compararTGA, va a ser donde guardamos los primeros 12 bytes leídos del archivo y los comparamos con los de cabezeraTGA. cabezera, guarda los 6 mas importantes bytes (largo, ancho y bpp).

int cargarTGA( char *nombre, textura *imagen) {
	GLubyte		cabezeraTGA[12]={0,0,2,0,0,0,0,0,0,0,0,0};	// Cabecera de un tga sin compresión
	GLubyte		campararTGA[12];					// Acá vamos a comprar la cabecera
	GLubyte		cabezera[6];						// Los 6 bytes importantes
	GLuint		bytesporpunto;						// Cuantos bytes hay por punto
	GLuint		tamanoimagen;						// Acá guardaremos el tamaño de la imagen
	GLuint		temp,i;							    // Variable temporal, y una para usar con el for
	GLuint		tipo=GL_RGBA;						// Nuestro tipo por defecto, lo veremos mas abajo

	FILE *archivo=fopen(nombre, "rb");	// Cargamos nuestro archivo en memoria
	if( archivo == NULL ||			// Existe nuestro archivo??
	    fread(compararTGA,1,sizeof(compararTGA),file)!=sizeof(compararTGA)	||	// Hay 12 bytes para leer??
	    memcmp(cabezeraTGA,compararTGA,sizeof(compararTGA))!=0		||	// Son iguales??
	    fread(cabezera,1,sizeof(cabezera),archivo)!=sizeof(cabezera)) {
		if(archivo==NULL)
			return 0;		// No se abrió el archivo
		else {
			fclose(archivo);
			return 0;
		}
	}
	/* Ahora hay que leer la cabecera del archivo, para saber cuanto es el 
       largo, ancho, y los bytes por puntos,
	   para eso acá hay una ilustración de la cabecera :
	   6 bytes -> xxxxxxx xxxxxxx xxxxxxx xxxxxxx xxxxxxx xxxxxxx	
		          |--- Largo ---| |---Ancho-----| |-bpp-|
	   El dato del largo se guarda en el cabezera[0] y cabezera[1], para 
       leerlo hay que multiplicar cabezera[1] por 256 y sumarselo a cabezera[0], 
       para leer ancho y bpp es el mismo procedimiento */
	imagen->largo=256*cabezera[1]+ cabezera[0];
	imagen->ancho=256*cabezera[3]+ cabezera[2];

	/* Ahora vemos si hay datos no válidos, como largo o ancho iguales menores a 0 o iguales a 0 */
	if( imagen->largo <= 0 ||	// Largo mayor que 0?? 
	    imagen->ancho <= 0 ||	// Ancho mayor que 0??
	    (cabezera[4]!=24 && cabezera[4]!=32)) {	// bpp es 24 o 32?? (solo se cargan 24 y 32 bpp)
		fclose(archivo);
		return 0;
	}
	imagen->bpp=cabezera[4];	    // Acá se guardan los bits por punto
	bytesporpunto=cabezera[4]/8;	// Acá los bytes por punto (1 byte = 8 bits)
	tamanoimage=imagen->largo * imagen->ancho * bytesporpunto;	
    // Esta es la memoria que necesitaremos para guardar los datos de la textura
	/* Ahora reservamos espacio en memoria para nuestra textura, luego leemos la 
       textura del archivo */
	imagen->dibujo = (GLubyte *)malloc(tamanoimagen);	// Reservamos la memoria necesaria para nuestra textura
	if(imagen->dibujo== NULL ||		// Se logró reservar la memoria???
	   fread(imagen->dibujo, 1, tamanoimagen, archivo) != tamanoimagen ) {	
        // Se lee, y se comprueba que lo leído es de la misma longitud que la asignada a a dibujo.
		if(imagen->dibujo != NULL)
			free(imagen->dibujo);
		fclose(archivo);
		return 0;
	}
	/* El formato tga guarda las imágenes en BGR, y opengl usa RGB,por lo cambiamos de lugares */
	for(i=0; i < int(imageSize); i+=bytesporpunto)
	{
		temp=imagen->dibujo[i];			           	// Guardamos el primer valor
		imagen->dibujo[i] = imagen->dibujo[i + 2];	// Asignamos el nuevo primer valor
		imagen->dibujo[i + 2] = temp;   			// Asignamos el ultimo valor
	}
	
	fclose (archivo);	// Cerramos el archivo
	
	/* Listo, terminamos con el código de carga, volvemos a opengl, ahora hay 
       que asignarle a la textura un ID, luego decirle a opengl cuales son el largo, 
       el ancho y los bpp */

	glGenTexture(1,&imagen->ID);	// Crea un ID para la textura, buscando un id que este vacío
	glBindTexture(GL_TEXTURE_2D, imagen->ID);	// Seleccionamos nuestra textura
	if(imagen->bpp ==24) tipo= GL_RGB;	// Si nuestra textura es de 24 bits, entonces 
                                        // se crea una textura rgb, sino una rgba
	/* Ahora creamos nuestra textura, entrando el largo, ancho y tipo */
	glTexImage2D(GL_TEXTURE_2D, 0, tipo, imagen->ancho, imagen->largo, 0, tipo, GL_UNSIGNED_BYTE, imagen->dibujo);
	/* Ahora le decimos a opengl como queremos que se vea nuestra textura, MAG_FILTER 
       es cuando la textura es mas grande que el lugar donde la asignamos, y MIG_FILTER, 
       es cuando la textura es mas pequeña que el lugar donde la asignamos, GL_LINEAR es 
       para que se vea bien tanto cerca como lejos, pero ocupa bastante procesador. 
       Otra opción el GL_NEARES, que ocupa menos procesador */
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);		// La m
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	return 1;	// Todo salió bien
}

Tenemos lista la función para cargar las texturas a memoria, ahora hay que decirle a opengl que vamos a usar texturas y debemos cargarlas.

void inicializar() {
	.
	.
	.
	glEnable(GL_TEXTURE_2D);
	if(!cargarTGA("textura.tga", texturas[0])) exit(); 
    // Cargamos la textura y chequeamos por errores
}

Listo, textura en memoria, ahora hay que usarla en algo.

void dibujar () {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);   // Se borra el buffer de la pantalla
	glRotatef(angulo, 0.0, 1.0, 1.0);	// Rotamos nuestro objeto
	glBingTexture(texturas[0]->ID);
	// Vamos a usar un cubo
	glBegin(GL_QUADS);
	/* La textura es una imagen cuadrada, ahora hay que asignarle los lugares donde va, 
       para eso esta glTexCoord2f(float x, float y) donde x e y son las coordenadas de la 
       textura que van a coincidir con el punto que vamos a definir */	
		glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0,  1.0);
		glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -1.0,  1.0);
		glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  1.0,  1.0);
		glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  1.0,  1.0);

		glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, -1.0);
		glTexCoord2f(1.0, 1.0); glVertex3f(-1.0,  1.0, -1.0);
		glTexCoord2f(0.0, 1.0); glVertex3f( 1.0,  1.0, -1.0);
		glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0, -1.0);

		glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  1.0, -1.0);
		glTexCoord2f(0.0, 0.0); glVertex3f(-1.0,  1.0,  1.0);
		glTexCoord2f(1.0, 0.0); glVertex3f( 1.0,  1.0,  1.0);
		glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  1.0, -1.0);

		glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, -1.0, -1.0);
		glTexCoord2f(0.0, 1.0); glVertex3f( 1.0, -1.0, -1.0);
		glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0,  1.0);
		glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0,  1.0);

		glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -1.0, -1.0);
		glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  1.0, -1.0);
		glTexCoord2f(0.0, 1.0); glVertex3f( 1.0,  1.0,  1.0);
		glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0,  1.0);

		glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, -1.0);
		glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0,  1.0);
		glTexCoord2f(1.0, 1.0); glVertex3f(-1.0,  1.0,  1.0);
		glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  1.0, -1.0);
	glEnd();
	angulo++;
	glutSwapBuffers();
}

Listo!!! tenemos texturas!! ahora casi podemos hacer un Quake IV!!! bueno... intenta.

Programas en C

Ficheros fuente de ejemplos y texturas "glut.zip" (55.169 bytes): (Alternativo: )

[Anterior] [Siguiente]