"Ningún juego dura tanto hasta que te lo terminas como el que te programas tú mismo,
ninguno te absorbe tanto y ninguno te produce tanta satisfacción cuando lo has acabado"
Programacion de videojuegos
Domingo 22 de Octubre del 2017

Menu principal
Colaborar (con PayPal)

Para continuar con el trabajo de esta Web y poder pagar el hosting, viene bien la ayuda que sea. Gracias a todos.

Importe: 

Ultimas descargas
19.Jan

Clase que permite dibujar texto en OpenGL con mucha facilidad.Usa FreeType2.Para ver que hace y c...


17. Multitexturas Imprimir Correo electrónico
Videojuegos - Curso de Programación de juegos
Escrito por Vicengetorix   
En este capitulo saldremos por fin de la version 1.1 de OpenGL y nos adentraremos en la version 1.3 (o anteriores con extension de multitextura).
Veremos las multitexturas, multiples texturas aplicadas a la misma superficie, y para ello usaremos GLee tal y como vimos en el capitulo anterior.
No sera un capitulo complicado si se ha llegado hasta este punto del curso pero sera un ejercicio liberador al dejar la vieja version 1.1 y abrirnos un mundo de capacidades graficas en nuestra tarjeta.

Lo primero que quiero comentar es que lo visto hasta ahora, aunque alguien lo pueda considerar desfasado como he visto en algun comentario por ahi, yo creo que es necesario para llegar hasta donde estamos del curso. Creo que si alguien que empieza se va a lo ultimo sin conocer nada de las bases, no entendera nada por listo que sea. Dicho esto, al tema.

Lo primero es lo primero, en este caso explicar el concepto de multitextura.
Imaginemos que no estamos con un PC delante y lo que vemos, en vez de un modelo tridimensional en pantalla, es una miniatura de plastico encima de la mesa que nos disponemos a pintar con nuestros botecitos de pintura y un pincel.
Le podriamos aplicar una primera capa de pintura como base. Eso seria en el PC aplicarle una textura.
Luego podriamos aplicarle otra capa de pintura con los colores. En la version PC aplicarle una segunda textura.
Por fin le aplicariamos otra capa de un color oscuro, pero muy diluido en disolvente para simular suciedad pero sin tapar totalmente la capa anterior. En la version PC del tema seria aplicarle una tercera textura.
La idea es esa.
Anteriormente podiamos aplicar una textura a los objetos de nuestro programa, que cargabamos desde disco. A partir de ahora podremos aplicar mas de una textura a cada superficie.
Alguien, llegado a este punto, se podria preguntar ¿para que hacer eso? Pues para temas avanzados como sombras, o para añadir suciedad a los modelos, o para otros mil efectos que se nos ocurran.

Mas facil es verlo con nuestros propios ojos. Dadas dos texturas como estas:







Conseguriamos esto al aplicarlas a la misma superficie, primero la primera y luego la segunda de forma que se mezcle con la anterior:



Creo que con esto el concepto no admite dudas.

El numero de texturas maximo que se puede aplicar depende de la implementacion de OpenGL, de la tarjeta grafica que tengamos. Con una llamada a la funcion glGetIntegerv(...) podremos saber cual es el maximo de nuestra tarjeta. Lo veremos. En mi caso, con una tarjeta Intel de hace unos años, el maximo es 8 texturas (de la 0 a la 7).

Antes de pasar al codigo debemos incluir el fichero GLee.c en nuestro proyecto, tal como se vio en el capitulo anterior.

Ahora empezaremos con el codigo, modificando el programa que teniamos en el capitulo 15 sobre carga de modelos y uso de Vertex Array. Concretamente con el codigo en que se cargaba un modelo de rinoceronte (al que daremos algo de color).
Partiendo de este codigo tenemos la posibilidad de ver como implementar multitexturas con Vertex Arrays y sin ellos.
Como siempre no pongo aqui todo el codigo si no solo el nuevo o el que cambia.

Empezamos con las definiciones globales que cambien en el codigo.

Lo primero es incluir el fichero de cabecera de GLee. Como internamente incluye gl.h, este ultimo ya no lo tenemos que incluir.

1
2
3
4
// Incluyo la cabecera de la libreria GLEE.
// Con eso ya estoy incluyendo gl.h
#include "glee/glee.h"
//#include <GL/gl.h> // Los ficheros de inclusion de OpenGL.

Añado una variable int mas para guardar el identificativo de una textura mas que voy a cargar, la de colores que voy a superponer al cartel y al rinoceronte.

1
2
// Añado tex3.
GLuint tex1, tex2, tex3; // Para los id's de texturas que genera OpenGL.

Cambio valores de los materiales que no me gustaban en capitulos anteriores (creo que me equivoque al ponerlos). Obviamente, esto no es importante para el tema que nos ocupa.

1
2
3
// Esto esta cambiado respecto anteriores versiones.
// (estaba equivocado)
GLfloat mat1_difusa[] = { 0.6, 0.6, 0.6, 1.0 };

1
2
3
4
// Esto esta cambiado respecto anteriores versiones.
// (estaba equivocado)
GLfloat mat2_difusa[] = { 0.0, 0.0, 0.4, 1.0 };
GLfloat mat2_especular[] = { 0.7, 0.7, 0.7, 1.0 };

Nos vamos a la funcion IniciaGL(), que aunque esta al final del codigo se ejecuta pronto.

Aqui verificaremos si la version de OpenGL es la que soporta multitexturas o si soporta la extension. En el codigo he simplificado y compruebo las dos cosas independientemente pero con una que se diera podriamos usar multitexturas.
Si la version es 1.3 o superior, la extension tambien estara soportada aunque las funciones y definiciones formen parte del nucleo (en ingles core).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Esta es la manera de intentar definir todo lo extra 
// de la version que se quiere usar.
// Si retorna true es que la tarjeta soporta la version
// y a partir de aqui ya se pueden usar las funcionalidades
// de la version normalmente. Si retorna false es que la
// version no es soportada.
if (!GLEE_VERSION_1_3)
{
// Si no se soporta mandamos un mansaje...
MessageBoxA(NULL, "OpenGL 1.3 NO soportada", "USW", MB_OK);
// ... salimos del programa y no hacemos mas.
PostMessage(IdVentana, WM_CLOSE, 0, 0);
return;
}
 
// Esta es la manera de definir y comprobar una extension.
// Si retorna true, apartir de aqui la tendremos disponible
// para su uso, si retorna false es que no esta soportada.
if (!GLEE_ARB_multitexture)
{
// Si no se soporta mandamos un mansaje...
MessageBoxA(NULL, "ARB_multitexture NO soportado", "USW", MB_OK);
// ... salimos del programa y no hacemos mas.
PostMessage(IdVentana, WM_CLOSE, 0, 0);
return;
}

Las multitexturas fueron incluidas en el nucleo de OpenGL de la version 1.3. Antes se soportaban a traves de la extension ARB_multitexture, en las tarjetas que lo soportaran.

Si trabajamos con version 1.3 o superior no sera necesario poner "ARB" o "_ARB" detras de las funciones o definiciones (por ejemplo glActiveTextureARB o GL_TEXTURE0_ARB), bastara con poner solo la funcion o la definicion solo (glActiveTexture o GL_TEXTURE0) ya que estan funciones forman parte del nucleo de OpenGL 1.3 y superiores.

Si por el contrario trabajamos con la extension por ser una version de OpenGL inferior a la 1.3 (1.1 o 1.2) deberemos añadir el sufijo correspondiente "ARB" o "_ARB" detras.

La libreria GLee, ademas de permitirnos comprobar la version o si esta soportada  una extension facilmente, en caso de respuesta positiva dejara preparadas para el el uso en nuestro programa las funciones correspondientes y las definiciones necesarias. Esto quiere decir que si intentamos usar una extension que sabemos que nuestra tarjeta soporta, antes de comprobar en el codigo si la soporta, el programa no reconocera las funciones y definiciones de esa extension. Solo podremos usarla despues de preguntar si la soporta. Esto es aplicable a las versiones. Es la forma de funcionar de GLee.

En todo caso, tras el codigo para comprobar si podemos usar multitextura, ya estamos en disposicion de usar las nuevas opciones.

Cargamos una textura nueva que sera la que superpongamos al cartel y a la piel del rinoceronte.

1
2
3
// Cargamos una textura para superponerla a 
// la textura normal.
CargaTextura(&tex3,"dibus//colores.jpg");

Tambien he añadido otro punto de luz para ver mejor (me ha dado por ahi). Ni que decir tiene que esto no es necesario si no se quiere.

1
2
3
4
5
6
7
// Pongo otra luz para ver mejor.
GLfloat posicion1[] = { -1.0, -3.0, 2.0, 1.0 };
glLightfv(GL_LIGHT1, GL_POSITION, posicion1);
glEnable(GL_LIGHT1);
glLightfv(GL_LIGHT1, GL_AMBIENT, ambiente);
glLightfv(GL_LIGHT1, GL_DIFFUSE, difusa);
glLightfv(GL_LIGHT1, GL_SPECULAR, especular);

En la funcion CargaTextura(...) he cambiado una cosa que estaba antes equivocada (mil perdones)
1
2
3
// Aqui habia antes un error. Ponia GL_TEXTURE_2D en vez de
// GL_TEXTURE_ENV
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);

Con esta rectificacion vereis que ahora si cambia la cosa de GL_MODULATE a GL_DECAL u otro parametro.

Vamos a la funcion PintaModelo() donde habra que cambiar el codigo para especificar con que textura trabajamos ya que ahora disponemos de mas de una para cada superficie que podemos usar o no.
Al trabajar con multitexturas, la antigua unica textura se convierte en la textura 0. Eso quiere decir que por defecto la textura 0 es la que modificamos si no especificamos nada (como antes de trabajar con multitexturas.
En cada unidad de textura (la 0, 1, 2, 3,...) se puede habilitar textura - glEnable(GL_TEXTURE_2D) - o desabilitar, se puede hacer bind de la textura que queramos y tengamos cargada; y se puede modificar la forma en que se aplicara sobre la anterior independientemente. Todo con las mismas funciones de siempre. Solo habra que especificar antes con que "texture unit" vamos a trabajar (la 0, 1, 2, 3,...).
Por defecto todas estan desabilitadas asi que despues de usarlas habra que tener cuidado de dejarlas como estaban, desabilitadas.

En caso de usar solo una textura, una sola unidad de textura, se debe usar la 0, la que siempre habiamos usado antes sin saberlo.
Si usamos mas seran siempre correlativas a la 0 sin dejar unidades de textura sin usar entre medias. En realidad puede ser que se pueda hacer especificando mas parametros de cada textura pero yo lo hago como os digo y no me complico mas. Si quereis investigar adelante.

Veremos la funcion glActiveTexture(...) que es la que usaremos para decir a OpenGL a que unidad de textura afectaran las proximas funciones sobre texturas de nuestro codigo.
La textura se especifica con las definiciones GL_TEXTURE0, GL_TEXTURE1, GL_TEXTURE2,... y asi hasta la ultima que nos permita nuestra tarjeta.

La funcion glClientActiveTexture(...) indica a que unidad de textura afectara la proxima funcion glEnableClientState(GL_TEXTURE_COORD_ARRAY) y glTexCoordPointer(...) cuando estemos usando Vertex Arrays.

La primera funcion glDisable(GL_TEXTURE_2D) se cambia por esto...

1
2
3
4
5
6
7
8
9
// Indico con cual textura voy a trabajar.
// Usaremos la 0 como textura propia del modelo.
// el "Client" indica que trabajamos con Vertex Arrays
// para las coordenadas de textura.
glActiveTexture(GL_TEXTURE0);
glClientActiveTexture(GL_TEXTURE0);
 
// Por defecto desabilito texturas.
glDisable(GL_TEXTURE_2D);

... con lo que aclaramos que la textura que desabilitamos es solo la 0 y a partir de este punto de la funcion las lineas de codigo sobre textura seran sobre la textura 0. Parece que al ser la textura por defecto la 0 no seria necesario especificarlo pero en realidad no sabemos si la funcion PintaModelo() ha sido llamada con la textura 0 como la activa. De hecho veremos en la funcion Pinta() que no va ha ser asi porque vamos a modificar la textura 1.

En la funcion Pinta() modificaremos la textura 1 para superponerla al modelo primero y al cartel despues.

Antes de pintar el modelo preparamos la textura 1, luego pintamos el modelo y luego dejamos la textura 0 como la de trabajo, no sin antes desabilitar la 1. El codigo esta explicado, como siempre.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Selecciono la textura 1 como la textura a la que afectaran 
// las siguientes funciones.
glActiveTexture(GL_TEXTURE1);
// Indico que usare vertex arrays para las
// coordenadas de la textura 1
glClientActiveTexture(GL_TEXTURE1);
// Habilito textura para la textura 1, antes seleccionada.
glEnable(GL_TEXTURE_2D);
// Indico que para esta textura 1 usare vertex arrays para
// las coordenadas de textura...
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// ... e indico donde estan esas coordenadas.
// Usaremos las mismas que para la textura que trae
// el modelo, aunque podrian ser otras.
glTexCoordPointer(2, GL_FLOAT, 0, uvTex);
// Indico cual de las texturas cargadas usare como la
// textura 1
glBindTexture(GL_TEXTURE_2D, tex3);
// y, aunque creo que es asi por defecto, indico que
// mezcle esta textura con lo que habia antes (la textura 0 y
// el color base del objeto)
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
 
// Pinto el objeto que sea.
PintaModelo();
 
// Tras pintar el objeto dejo la textura 1
// desabilitada para que lo que pinte despues
// no la use (a no ser que indique otra cosa especificamente)
glActiveTexture(GL_TEXTURE1);
glClientActiveTexture(GL_TEXTURE1);
glDisable(GL_TEXTURE_2D);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
// e indico al programa que las futuras operaciones de textura
// sean contra la textura 0 (la que se usa por defecto si usas
// solo una textura.
glActiveTexture(GL_TEXTURE0);
glClientActiveTexture(GL_TEXTURE0);

Dentro de la funcion PintaModelo() se preparaba la textura 0 para pintar la textura que trae el modelo con lo que a la hora de pintar el modelo tendremos la textura 0 habilitada con la imagen de la piel del rinoceronte y la textura 1 con la imagen de colores que vamos a superponer.

El siguiente codigo modificado sera el de pintar el cartel. Para este no se usan Vertex Arrays, con lo que no usaremos ahora la funcion glClientActiveTexture(...), no hara falta. El resto es parecido. Solo cambia la especificacion de las coordenadas de textura, para lo que usaremos una nueva funcion, glMultiTexCoord2f(...), con la que especificaremos a que textura se aplicaran las coordenadas y las propias coordenadas.

Antes de pintar y despues de preparar la textura de siempre de la manera de siempre (la textura 0) preparamos la textura 1.

1
2
3
4
5
6
7
8
9
10
11
// Indico que las siguientes funciones son 
// contra la textura 1
glActiveTexture(GL_TEXTURE1);
// Habilito textura 1
glEnable(GL_TEXTURE_2D);
// Le digo que use como textura 1 la cargada
// en tex3.
glBindTexture(GL_TEXTURE_2D, tex3);
// Indico que mezcle la textura con lo anterior
// (textura 0 y color de base del objeto).
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);

Un poco despues, cuando pintamos los vertices especificamos las coordenadas de la textura 1 con la nueva funcion glMultiTexCoord2f(...).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
glBegin(GL_QUADS);
glTexCoord2f(0.0,0.0);
 
// Debo indicar las coordenadas de textura para
// la textura 1, la que superpondremos a la textura
// normal.
glMultiTexCoord2f(GL_TEXTURE1, 0.0,0.0);
 
glVertex3i( 0, 0,0);
glTexCoord2f(0.0,1.0);
 
// Coordenadas para la textura 1
glMultiTexCoord2f(GL_TEXTURE1, 0.0,1.0);
 
glVertex3i( 0, 75,0);
glTexCoord2f(1.0,1.0);
 
// Coordenadas para la textura 1
glMultiTexCoord2f(GL_TEXTURE1, 1.0,1.0);
 
glVertex3i( 300,75,0);
glTexCoord2f(1.0,0.0);
 
// Coordenadas para la textura 1
glMultiTexCoord2f(GL_TEXTURE1, 1.0,0.0);
 
glVertex3i( 300,0,0);
glEnd();

Observad como las coordenadas de la textura 0 se indican como siempre, con glTexCoord2f(...). Se podria hacer tambien con la nueva funcion glMultiTexCoord2f(...) indicando como primer parametro GL_TEXTURE0, seria lo mismo.

Para terminar, como siempre, dejamos las cosas en su sitio.

1
2
3
4
5
6
// Tras terminar de pintar con dos texturas
// desabilito la textura 1 y dejo la 0 para
// futuras operaciones.
glActiveTexture(GL_TEXTURE1);
glDisable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);

Ya tenemos el cartel con colorines por encima.

Para terminar los cambios en la funcion Pinta() vamos a preguntar a nuestra tarjeta grafica cuantas capas de pintura acepta, cuantas texturas se pueden aplicar a la vez a la misma superficie.

1
2
3
4
5
6
7
8
9
// Escribo en pantalla el numero de 
// texturas que se pueden superponer en mi
// tarjeta grafica.
int texu;
// Esta funcion es la que obtiene el dato y lo
// deja en una variable.
glGetIntegerv(GL_MAX_TEXTURE_UNITS, &texu);
// Esto es lo que escribo.
Escribe(370,20,"Maximo de texturas superpuestas: %i",texu );

Por ultimo, en la funcion ProcesaMensajes(...), en el punto en que borro las texturas cargadas debo añadir el borrado de la textura de colorines nueva que he cargado en este capitulo, la de tex3.

1
2
3
4
5
// Borro las texturas de memoria. No hay que dejar basura.
glDeleteTextures( 1, &tex1 );
glDeleteTextures( 1, &tex2 );
 
glDeleteTextures( 1, &tex3 );

Al ejecutar el programa se veria algo asi:



El codigo completo seria: usw17.cpp






¡Sólo los usuarios registrados pueden escribir comentarios!
+/- Comentarios
Buscar
Jesus  - Me sale esto :/   |Registered |09-08-2015 04:37:41
1>LINK : fatal error LNK1104: cannot open file 'LIBC.lib'
Black M4ster  - Ayuda porfavor     |88.17.187.xxx |01-04-2013 16:31:04
Vinculando...
1>main.obj : error LNK2001: símbolo externo
_GLeeFuncPtr_glClientActiveTexture sin resolver
1>main.obj : error LNK2001:
símbolo externo _GLeeFuncPtr_glActiveTexture sin resolver
1>main.obj : error
LNK2001: símbolo externo _GLeeFuncPtr_glMultiTexCoord2f sin resolver
1>main.obj
: error LNK2001: símbolo externo __GLEE_ARB_multitexture sin
resolver
1>main.obj : error LNK2019: símbolo externo _GLeeEnabled sin resolver
al que se hace referencia en la función "void __cdecl IniciaGL(void)"
(?IniciaGL@@YAXXZ)
1>main.obj : error LNK2001: símbolo externo
__GLEE_VERSION_1_3 sin resolver
1>E:\C++\OpenGL\Multitexturas
\Debug\Multitexturas.exe : fatal error LNK1120: 6 externos sin
resolver
1>El registro de compilación se guardó en el
"file://e:\C++\OpenGL\Multitextura
s\Multitexturas\Debug\BuildLog.htm" ;
1>Multitexturas - 7
errores, 67 adverten...
Vicengetorix   |87.221.227.xxx |02-04-2013 20:16:43
No encuentra glee.lib al linkar.
Asegurate de que esta librería esta disponible
al linkador (en el directorio lib de VC, por ejemplo)
Black Master  - Graciasss     |88.17.187.xxx |03-04-2013 15:18:08
Muchisimas graciaas ya está xD

3.26 Copyright (C) 2008 Compojoom.com / Copyright (C) 2007 Alain Georgette / Copyright (C) 2006 Frantisek Hliva. All rights reserved."

 


Banner
Spanish Chinese (Simplified) English French German Japanese Portuguese Russian