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.
Ficheros fuente de ejemplos y texturas "glut.zip" (55.169 bytes):
(Alternativo:
)
© Mayo de 2.001, Con Clase, webmaster@conclase.net