7. Texturas |
![]() |
![]() |
Videojuegos - Curso de Programación de juegos | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Escrito por Vicengetorix | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
![]() Por si alguien no lo tiene claro explicaremos lo que es una textura. Tenemos una imagen digital en el ordenador. Puede ser una foto que nos hemos descargado de nuestra camara, de las ultimas vacaciones, o un dibujo que hemos escaneado o un borratajo que hemos hecho en el Paint de Windows. Esta imagen puede estar en cualquier formato de imagen digital como BMP, GIF, JPG, TGA, PNG, ... (son las extensiones de los archivos). Pues cualquier imagen asi puede ser una textura y puede ser usada en nuestro programa. Esta imagen se carga desde el disco (normalmente con una libreria especializada pero podriamos programarlo nosotros), se le da un identificativo y la forma de usarla a OpenGL y se utiliza de relleno de los poligonos que pintamos en OpenGL, en vez de usar un color plano como hemos hecho en anteriores capitulos. La forma de aplicar las imagenes a los poligonos son muchas: una imagen por cuadrado, una imagen entera que ocupa varios triangulos, una imagen repetida varias veces horizontal y verticalmente a modo de mosaico,... En este caso usaremos la primera opcion. Mas adelante veremos mas. Veremos ahora una serie de conceptos sobre texturas y OpenGL. OpenGL no carga nada desde disco, ni texturas ni modelos 3D hechos con programas de diseño 3D. Por eso usamos Corona para la carga de texturas. Mas adelante veremos carga de modelos. Las texturas deben tener medidas que sean 16, 32, 64, 128, 256, 512, 1024, ... potencias de 2. ¿Por que? Pues porque OpenGL esta hecho asi, y si no seguimos esta norma los programas no funcionaran o se pararan. Mas tarde se han habilitado extensiones para evitar esto pero no cuesta nada modificar el tamaño de nuestras texturas. Llegado a este punto pensaras que habra imagenes que esten achatadas por ejemplo. Pues no porque aunque uses una textura achatada si la aplicas a un cuadrado con el tamaño correcto se vera bien. Las texturas en OpenGL pueden ser de 1 dimension (1D), 2 dimensiones (2D) o 3 dimensiones (3D). Nosotros usaremos las de 2D (una imagen normal con ancho y alto) Estas son las dos imagenes que vamos a usar en este ejemplo: ![]() ![]() Las dos con medidas potencia de 2. El cuadrado decorativo es de la web CGTextures. Para cargar las imagenes en nuestro programa usaremos la libreria Corona ya que ademas de ser gratuita, es muy facil de usar y carga los tipos de imagen mas importantes. Para usar Corona copiaremos el fichero corona.lib en el directorio de librerias del Visual C++ y copia el fichero corona.dll en el directorio de tu programa. Puede ser que al linkar de conflicto por que ya estan definidos simbolos. Eso se arrega metiendo "MSVCRT" en el apartado de propiedades del proyecto, en "vinculador-omitir biblioteca especifica". A mi me ha pasado. En el codigo incluiremos:
con lo que añadimos la libreria y el fichero de definiciones y ya podemos usar Corona. Tambien sacaremos de la funcion IniciaGL() la variable RECT rect y la convertimos en global para su uso desde cualquier parte del programa. Definimos tambien de forma global dos GLuint (tipo unsigned int pero para OpenGL) para los identificativos de nuestras texturas.
Empezamos con los cambios en la funcion IniciaGL(). Lo primero es que desaparece la definicion de rect, que habiamos hecho global, el resto lo añadimos al final. Definimos un puntero a un objeto image de Corona, que nos servira para cargar las texturas desde disco.
Luego cargamos la imagen desde disco indicando a Corona el fichero y el tipo de imagen a la que queremos que la convierta, en nuestro caso R8G8B8A8 que no quiere decir mas que cada pixel de la imagen estara formado por 8 bits de rojo(R), 8 bits de verde(G), 8 bits de azul(B) y 8 bits de canal Alfa que se usa para transparencias. Este formato es usado por OpenGL y por eso lo hacemos.
Ahora genero el identificativo OpenGL de nuestra pimera textura en una de las variables que defini de forma global como GLuint. El primer parametro es el numero de identificativos de textura a generar. Nosotros lo haremos de una en una. El segundo parametro es la direccion de la variable donde dejara el identificativo.
Ahora le digo a OpenGL que voy a usar esta textura cuyo identificativo he creado hasta que no diga lo contrario. A partir de aqui las funciones sobre texturas seran contra ella. El primer parametro es el tipo de textura (2D) y el segundo la variable donde esta el identificativo.
Ahora debo pasarle a OpenGL la informacion de la imagen a usar como textura, sobre todo los pixels que la forman para que la guarde en la memoria de video (o no, el lo gestionara a partir de aqui). Lo haremos ahora con la funcion gluBuild2DMipmaps, una funcion de la libreria GLU que nos facilita la vida porque nos hace mipmaps de la imagen automaticamente. Pero, ¿que son los mipmaps?. Pues cogemos una imagen y hacemos, partiendo de ella, una serie de imagenes iguales pero cada vez mas pequeñas. Y ¿para que? pues para que cuando presentemos esa imagen muy lejana no tengamos que tratar la imagen original entera inutilmente, ya que desde esa distancia no se va apreciar y nos consumira mas tiempo. Es una forma de que nuestro programa vaya mas rapido, y gracias a dios podemos hacer eso solo con una sola funcion. En este grafico se ve mas claro: ![]() Los parametros de la funcion gluBuild2DMipmaps son:
Despues van una serie de funciones que definen como se aplicara esa textura a la hora de usarla en poligonos y en pantalla. La primera es como va a calcular el color de un pixel en funcion de si el pixel correspondiente de la imagen debe cubrir un area mayor en pantalla o si debe cubrir un area menor (por ejemplo: una imagen cargada en disco de 16 x 16 la asignamos a un cuadrado que en pantalla tendra 100 x 100. O al contrario una imagen de disco de 512 x 256 la aplicamos a un cuadrado que en pantalla sera de 20 x 10). En nuestro programa pondremos:
Una funcion para cada caso. Los posibles valores son combinaciones de LINEAR y NEAREST porque en este caso estamos trabajando con mipmap que complica el asunto. En la siguiente textura lo veremos mas claro, LINEAR suaviza la imagen por que interpola valores de los pixels adyacentes y NEAR la hace mas escalonada. Solo queda definir el modo en que se aplica la textura sobre el color del poligono. DECAL y REPLACE simplemente de pone el dibujo en el poligono sin importar el color de este. MODULATE y BLEND se podrian usar con iluminacion uno, ya que modifica la textura segun el color del poligono y para transparencia el otro. Ahora vamos a usar DECAL:
Ya solo queda borrar el objeto imagen de Corona, ya que tenemos cargado en OpenGL la imagen.
Cargamos despues la segunda imagen con el puntero a imagen Corona que temenos ahora libre y de forma analoga a la primera imagen, generamos identificativo de textura en la otra variable que habiamos definido y la seleccionamos para trabajar con ella:
Esta vez no generamos mipmaps y usaremos el metodo normal de OpenGL, la funcion glTexImage2D que carga la textura desde el puntero que le pasamos y la guarda para su uso posterior. Los parametros de esta funcion son:
Ahora, igual que antes, la forma en que aplicara esta textura. El MIN_FILTER y MAG_FILTER pero esta vez es mas sencillo, solo hay dos opciones: GL_LINEAR, opcion con la que interpola el color con los pixels adyacentes y da un resultado suavizado. GL_NEAREST, opcion con la que elegira simpemente el color del pixel mas cercano y se vera la imagen escalonada. Es cuestion de probar en cada caso.
Luego, lo mismo que en la textura anterior, la forma en que se aplica el color de cada pixel de la textura sobre el poligono (cuadrado, triangulo, ...), si tiene en cuenta y como el color de fondo del poligono. En nuestro caso solo tendra en cuenta el color de la textura.
Para terminar borramos el objeto imagen de Corona que habiamos creado al cargar la textura del disco, y que ya no nos hace falta porque la tenemos cargada y gestionada por OpenGL.
Ya hemos terminado con la carga y definicion de las texturas, y estamos preparados para usarlas, para pintar con ellas. Vamos a modificar la funcion Pinta(). El primer cambio es cambiar la posicion del objeto a pintar (era un cubo) para que quede mejor en pantalla.
Despues de mover y rotar el objeto que todavia no hemos pintado le decimos a OpenGL que vamos a usar texturas para aplicarlas a el objeto que dibujemos (podriamos hacerlo antes que mover, rotar y escalar. Da igual). Activamos el modo textura 2D:
Le decimos cual de las texturas usar:
Ahora le pasamos a OpenGL los datos del cubo como haciamos en el capitulo anterior pero con dos cambios. Quitamos las funciones glColor, ya que no vamos a usar color sino textura, e incluimos la funcion glTexCoord2d o glTexCoord2f antes de cada funcion glVertex. Esto quiere decir que le damos a OpenGL las coordenadas de textura de cada vertice antes de darle las coordenadas del vertice... ¿Coordenadas de textura? pues si. Haremos un parentesis para explicar este concepto, ya que es muy importante. Una textura (2D) es una imagen formada por un cuadrado de x pixels por y pixels. En el caso de OpenGL x e y se usan para colocar puntos en el espacio 3D junto con z, asi que para no provocar confusiones, en el caso de una textura, diremos que es un cuadrado de s por t pixels (en vez de x por y). Para complicarlo mas y como OpenGL hace continuamente uso masivo de datos float o double (coma flotante) tomaremos como alto y ancho de cualquier textura, sea del tamaño que sea, 1.0 y las coordenadas iran de 0.0 a 1.0 (pasando, por ejemplo, por 0.1, 0.2, 0.345, 0.592235, 0.92934405, ...). Asi la coordenadas de las esquinas de cualquier textura seran en formato (s, t):
y el centro de la textura sera (0.5, 0.5). Graficamente: ![]() ![]() Todo esto ¿para que?. Pues para que podamos hacer coincidir nuestro vertice con la parte de la textura que queramos de forma que, jugando con las coordenadas de textura, usar para relleno de un poligono, solo una parte de la imagen de la textura o repetir la imagen n veces dentro del poligono. Mas tarde lo veremos. Asi quedaria el codigo que envia las coordenadas de nuestro cubo a OpenGL:
Con esto ya habremos puesto en pantalla un bonito cubo pintado con nuestra textura. Ahora vamos a poner un cartel a modo de titulo en 2D, tal y como vimos en el capitulo 5, pero con textura. Pondremos el logo de UnSitioWeb. Para ello, despues de pintar el cubo, cambiamos la perspectiva para pintar en ortogonal. Cambiamos a la matriz de proyeccion:
y para no tener que definir mas tarde la proyeccion en perspectiva otra vez, guardamos la matriz actual de proyeccion... si, si esto es nuevo tambien. Habra que explicarlo. OpenGL permite guardar matrices en una pila. Si en una pila igual que la programacion en codigo maquina (ensamblador). Por culturilla explicare que es una pila para quien no lo sepa. Una pila es una estructura de datos de tipo LIFO (Last In, First Out), que no es otra cosa que el ultimo dato guardado es el primero que podemos sacar. De forma figurada es como un tubo de patatas fritas (no voy a decir la marca que no me pagan). Lo vamos llenando de patatas, pero cuando nos las comemos, lo hacemos en orden inverso a como las metimos. Bien; OpenGL tiene pila para las matrices y hay dos importantisimas funciones para gestionarla. glPushMatrix() que "empuja" la matriz que tenemos seleccionada (glMatrixMode) a la pila de matrices (en realidad la copia), y glPopMatrix() que recupera de la pila una matriz y la carga en la matriz que tenemos seleccionada para trabajar con ella. Esto nos permite guardar temporalmente el contenido, por ejemplo, de la matriz de proyeccion, modificarla para pintar de otra forma, y cuando hemos terminado, recuperarla y seguir pintando como si no la hubieramos modificado. Esto es lo que vamos ha hacer en este capitulo del curso. Lo que haremos sera:
Este es el codigo correspondiente:
Seleccionamos la textura que vamos a usar ahora:
y habilitamos el blending ... Si otra explicacion de algo nuevo. El blending es una funcionalidad de OpenGL que permite una serie de operaciones usando el color que ya esta en el pixel de pantalla que se va a pintar (destino) y el color del pixel de nuestro objeto que estamos pintando. Lo mas interesante para nosotros es que permite usar el canal Alfa de nuestra textura. (recordad el formato de cada pixel - RGBA). Si hemos cargado una textura con canal Alfa (no todos los formatos graficos lo permiten, nosotros usamos PNG que si lo usa), lo usaremos para hacer transparente el fondo de la imagen (el logo de UnSitioWeb). Asi no veremos todas las imagenes como rectangulos (no se ve igual USW que USW ). Las combinaciones son muchas pero a nosotros solo nos interesa por ahora el tema de la transparencia. En nuestro codigo, primero habilitamos el blending:
y luego le decimos a OpenGL como lo queremos hacer. Para las transparencias usaremos una opcion que usa el canal Alfa para indicar el grado de transparencia del pixel, de forma que podemos hacerlo totalmente transparente hasta totalmente opaco, pasando por 255 grados de transparencia. Esta opcion va bien con los PNGs de fondo transparente que crean los programas de graficos:
A partir de ahora todas las texturas que pintemos se veran afectadas por el canal Alfa, si lo tuvieran (una imagen sin canal Alfa de origen, un JPG por ejemplo, al cargarla con la opcion de R8G8B8A8 de Corona, le crea un canal Alfa sin ninguna transparencia). Despues pintaremos un cuadrado con la textura que habiamos cargado con el logo (ya habiamos indicado a OpenGL que la textura tex2 pasaba a ser la activa antes de lo del blending) Trasladamos 10 pixels abajo y 10 pixels a la derecha para que no este muy pegado al borde:
y pintamos el cuadrado con sus coordenadas de textura y de vertices:
Una vez pintado:
En resumen, dejo todo como antes de pintar el cartel:
Termino la funcion Pinta() presentando lo pintado en pantalla como siempre. Ya solo nos queda un pequeño detalle pero no por eso deja de ser importante. Habiamos borrado ya de memoria las imagenes cargadas por Corona porque no hacian falta pero nos quedan por borrar el espacio de memoria que ocupan nuestras texturas OpenGL cuando salgamos del programa. En la funcion ProcesaMensajes(...) dentro de la opcion que trata el mensaje WM_DESTROY (destruccion de la ventana) antes de terminar el programa con PostQuitMessage(0) incluyo el borrado de mis texturas:
Al final el trabajo se ve recompensado al ejecutar nuestro programa: ![]() El codigo completo seria:Â usw7.cpp Aunque este capitulo este terminado, voy a sugerir modificaciones al programa para probar lo que sucede al cambiar cosas. Lo primero sera eleminar el blending de el cartel de UnSitioWeb para ver que pasa. Lo unico que habria que hacer es comentar estas lineas (en realidad bastaria con comentar la primera):
Veremos como lo que no deberia aparecer nos tapa parte del cubo. Lo segundo sera modificar las coordenadas de textura de dos caras del cubo. En la cara frontal vamos a hacer un mosaico con la repeticion de la textura dentro de una cara y para ello vamos a incluir dos funciones en IniciaGL() que indican a OpenGL que si las coordenadas de textura son mayores de 1.0 repita la textura en ese eje (si fuera 2.0 la repetiria 2 veces, si fuera 8.0 la repetiria 8 veces, si fuera 1.5 la repetiria 1 vez y media, ...). Creo que es la opcion por defecto pero yo prefiero no correr riesgos. Lo pondriamos en la parte del programa en que indicamos a la textura como se tiene que comportar:
Despues no tendriamos mas que cambiar las coordenadas de textura de una cara para ver el efecto:
Observese que en la funcion glTexcoor hemos puesto un 3 donde antes habia un 1. Tendremos ahora un mosaico de 9 cuadros iguales (3 x 3). Se puede jugar con los numeros para ver otros efectos. Veriamos esa cara asi: ![]() En la cara izquierda vamos a usar solo el centro de la textura para recubrirla y para eso no necesitamos mas que modificar las coordenadas de textura de esta cara:
Como se ve en el codigo solo hemos cambiado el 1.0 por 0.6 y el 0.0 por 0.4; hemos seleccionado un cuadrado del centro de la textura y el resultado sera este: ![]() Y con esto es bastante para este capitulo. El proximo sera de luces. Por si se nos ha pasado el enlace al codigo ahi va otra vez: usw7.cpp ¡Sólo los usuarios registrados pueden escribir comentarios!
3.26 Copyright (C) 2008 Compojoom.com / Copyright (C) 2007 Alain Georgette / Copyright (C) 2006 Frantisek Hliva. All rights reserved." |