"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"
Este capÃtulo habla del billboarding con OpenGL y ya de paso se verán asuntos que serán muy útiles sobre el espacio tridimensional. Además los billboards son muy usados, desde para simular árboles o hierba, a los sistemas de partÃculas, pasando por otros usos varios. Veremos 4 tipos de billboards.
Lo primero es lo primero, el concepto de billboard. La palabra significa "cartelera".
Un billboard es un gráfico en dos dimensiones situado en un mundo de tres dimensiones y orientado de manera que mire hacia la cámara y siempre se vea de frente.
Para lograr esto tendremos, claro está, que girar el cartel cuando giremos o movamos la camara, o cuando movamos el propio cartel.
Una vez claro el concepto veremos tipos de billboard. La primera clasificación es entre los que se orientan hacia la cámara y los que se orientan hacia el plano perpendicular a la dirección en que mira la cámara, y en el que se encuentra la cámara. Uffff, me ha salido un poco complicado. Imaginemos que la cámara está en un muro. Los carteles se orientan hacia el muro, no hacia la cámara. Si la cámara gira, girará el muro entero de forma que la cámara siempre mira de forma perpendicular al muro y los carteles tendrán que reorientarse al girar el muro. Gráficamente se verán mejor las dos opciones:
Orientados a la cámara:
Orientados al plano de la cámara:
¿Mejor as�. Realmente lo que los diferencia es que el billboard orientado a cámara gira cuando la cámara se mueve y el orientado al plano de la cámara gira cuando la camara gira.
Para determinar la dirección de un cartel manejaremos 3 tipos de vectores. Primero el que indica la dirección donde está la cámara respecto a la posición del cartel, el de dirección. El que indica la dirección hacia arriba del cartel. Si el cartel está vertical coincidirá con el vector que marca el eje Y (0,1,0), si no está vertical será distinto. Y por último el vector que indica la dirección de la derecha del cartel y que será perpendicular al plano donde están los otros dos. Todos estos vectores normalizados, claro (longitud 1).
Observad que solo cambia el vector que marca hacia arriba.
Jugando con estos 3 vectores podremos orientar el cartel. Lo haremos gracias a como OpenGL trata las matrices. Veamos la composición de la matriz de modelo.
Conociendo esto podemos modificar la matriz de modelo para que nuestros objetos 3D se orienten hacia donde queramos.
Un nuevo identificador de textura para el billboard.
1 2
// Añado tex4 para el billboard. GLuint tex1, tex2, tex3, tex4;// Para los id's de texturas.
Añado una variable para mover la cámara a lo largo del eje X con las teclas de flecha izquierda y derecha.
1 2 3 4
// Variable para la coordenada X de la camara. // La modificaremos segun las teclas de flecha // izquierda o derecha. GLfloat lat=0;
Añado una función llamada MulVecMat(...) que sirve para multiplicar un vector por una matriz. Esto sirve para aplicar las transformaciones que incluye esa matriz al vector (rotarlo, girarlo y escalarlo). La usaremos luego para averiguar la posición real del billboard.
1 2 3 4 5 6 7 8 9 10 11 12
// Funcion para multiplicar un vector por una matriz, matriz // tal cual la racuperamos con glGetFloatv(GL_MODELVIEW_MATRIX,xx) // por ejemplo. Modifica el vector que le pasamos. void MulVecMat(GLfloat v[3], GLfloat m[16]) { GLfloat x[3]; Â x[0]=(m[0]*v[0])+(m[4]*v[1])+(m[8]*v[2])+m[12]; x[1]=(m[1]*v[0])+(m[5]*v[1])+(m[9]*v[2])+m[13]; x[2]=(m[2]*v[0])+(m[6]*v[1])+(m[10]*v[2])+m[14]; v[0]=x[0]; v[1]=x[1]; v[2]=x[2]; }
En la función IniciaGL().
Cambiamos el color negro del fondo por azúl. De esta forma, pintando el cuadrado entero del billboard para apreciar mejor como actua, lo veremos mejor.
1 2
// Cambio el color del fondo. glClearColor(0,0,0.6f,1.0f);
Cargo la textura del billboard.
1 2
// Cargo textura para el billboard. CargaTextura(&tex4,"dibus//Imagen1.png");
Ajusto las posiciones de las luces.
1 2
// Ajusto posicion de luz. GLfloat posicion[]={2.0, 2.0, 0.0, 1.0};
1 2
// Ajusto las posiciones de la luz. GLfloat posicion1[]={9.0, 3.0, 0.0, 1.0};
En la función Pinta() está el meollo del asunto.
Primero he cambiado un parametro de la perspectiva.
1 2 3
// El primer parametro es ahora 60 en vez de 50. No muy es importante // el cambio. gluPerspective(60.0f, (float)rect.right/(float)rect.bottom, 0.5f, 50.0f);
// He cambiado el orden de los giros porque // me parecio que queda mejor. // Giro arriba o abajo glRotatef(ang_camaraY,1,0,0); // Giro a izquierda o derecha glRotatef(ang_camaraX,0,1,0);
En el movimiento de la cámara incluyo la variable "lat" para el movimiento lateral, en el eje X, y elevo el punto de vista (subo la posición de la cámara).
1 2 3 4 5
// Incluyo la variable lat para modificar // la posicion X de la camara. // Elevo, tambien, el punto de vista. // Me alejo o acerco de la escena. glTranslatef(lat,-10,-3-dist);
Elimino el giro que dábamos a los objetos para que se vieran mejor. Con esto se quedan todos en el suelo (Y=0).
1 2 3
// Giro el objeto 30 grados en el eje x, luego otros // 30 en el eje y. Es para que quede bonito. //glRotatef(30,1,0,0);
Modifico la variable "lat" en función de la pulsación de teclas.
1 2 3 4 5 6 7 8 9 10 11 12 13
// Con las teclas de flecha izquierda y // flecha derecha, puedo mover la camara // en el eje X (la variable "lat"). if(Teclas[VK_RIGHT]) { lat+=0.2; if(lat>20) lat=20; } if(Teclas[VK_LEFT]) { lat-=0.2; if(lat<-20) lat=-20; }
Llegamos al pintado del primer cartel o billboard. Será orientado a la cámara.
Guardo la matriz de modelo ya que voy a cambiarla y quiero usarla luego para colocar el segundo billboard. Coloco el cartel donde quiero que salga. Defino variables que voy a usar para guardar vectores y la matriz de modelo.
// Pintado del primer billboard. // Este lo haremos calculando los vectores // que marcan la direccion a la camara. Â // Guardo la matriz de modelo // antes de cambiarla. glPushMatrix(); // Subo 1 en el eje y para pintar // el billboard encima del ultimo // cubo que pinte. glTranslatef(0,1,0); Â // Para el vector que marca la // direccion de arriba del billboard. GLfloat arr[3]; // Para el que marca la derecha. GLfloat der[3]; // Para el que marca la direccion hacia la // camara. GLfloat dir[3]; // Uno auxiliar. GLfloat aux[3]; // Espacio para guardar la matriz de modelo GLfloat mv[16];
Extraigo la matriz de modelo y obtengo la posición real que marca la matriz multiplicando esta matriz por un vector nulo (0,0,0). Con esto aplico al vector 0,0,0 las transformaciones de la matriz.
1 2 3 4 5 6 7 8 9
// Obtengo la matriz de modelo actual. glGetFloatv(GL_MODELVIEW_MATRIX, mv); // El vector auxiliar lo pongo en el eje de // coordenadas, a 0. aux[0]=0; aux[1]=0; aux[2]=0; // Multiplico el vector a 0 por la matriz // de modelo para obener la posicion actual, despues de // haber rotado y movido. La posicion del billboard. MulVecMat(aux, mv);
// Me monto el vector de direccion restando de la // posicion de la camara, la del billboard. dir[0]=-lat-aux[0]; dir[1]=10-aux[1]; dir[2]=(3+dist)-aux[2]; // Normalizo el vector. Normaliza(dir); // Pongo un vector de direccion arriba generico, // el que marca la direccion arriba del espacio. aux[0]=0; aux[1]=1; aux[2]=0; // Hago el producto vectorial de el vector hacia arriba (aux) // y el vercor de direccion a la camara(dir), con lo que // obtengo el vector que marca a la derecha del billboard. ProdVectorial(aux,dir,der); // Lo normalizo. Normaliza(der); // Hago el producto vectorial de el vector de direccion a la // camara(dir) con el que marca la derecha(der) y obtengo // el vector que marca la direccion arriba real del billboard. ProdVectorial(dir,der,arr); // Lo normalizo. Normaliza(arr);
// Guardo el vector que marca la derecha que hemos obtenido // en la posicion correspondiente de la matriz de modelo. mv[0]=der[0]; mv[1]=der[1]; mv[2]=der[2]; // Hago lo mismo con el vector que marca la direccion arriba. // Haciendo esto, el billboard es de tipo esferico. // Si en vez de el vector obtenido antes pongo el vector // hacia arriba generico, el que marca el eje Y (0,1,0), el // billboard sera de tipo cilindrico. mv[4]=0;//arr[0]; mv[5]=1;//arr[1]; mv[6]=0;//arr[2]; // Por ultimo guardo el vector de direccion a la camara // en la posicion correspondiente de la matriz de modelo. mv[8]=dir[0]; mv[9]=dir[1]; mv[10]=dir[2]; // Y con la matriz modificada en la parte de giro, la cargo en // en OpenGL. glLoadMatrixf(mv);
Finalmente pinto el cartel usando coordenadas X e Y, con la textura que quiera, usando un PNG con transparencia. Gracias a la matriz que hemos cargado, el cartel se colocará en su sitio pero siempre de cara a la cámara.
// Ahora solo queda dibujar el billboard con la textura // que hayamos cargado // Habilito texturas glEnable(GL_TEXTURE_2D); // Hago bind de la textura que voy a usar glBindTexture(GL_TEXTURE_2D, tex4); // Indico como aplicar la textura glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); // Habilito blending para que las partes transparentes de // de la textura no se pinten, y la funcion adecuada. // Si comentamos esta linea, veremos el cuadrado entero para // poder apreciar como se comporta el billboard. glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Pinto el cuadrado, siempre usando solo coordenadas X e Y. // La coordenada Z sera siempre 0. // Aqui tendremos que tener cuidado de ajustar las coordenadas al // tamaño que queremos del billboard y donde esta el centro sobre // el que rotara al mover la camara. glBegin(GL_QUADS); glTexCoord2f(0.0,1.0); glVertex3f(-1, 0, 0); glTexCoord2f(1.0,1.0); glVertex3f(1, 0, 0); glTexCoord2f(1.0,0.0); glVertex3f(1, 2, 0); glTexCoord2f(0.0,0.0); glVertex3f(-1, 2, 0); glEnd(); // Dejamos las cosas como queremos. glDisable(GL_BLEND); glDisable(GL_TEXTURE_2D); // Restablezco la matriz de modelo anterior a modificarla // para el billboard. glPopMatrix();
Pintaremos ahora el segundo billboard. Este será orientado al plano de la cámara
Coloco la posición donde quiero que se pinte. Defino un buffer para guardar la matriz de proyección (el de la matriz de modelo lo creamos antes). Obtengo la matriz de modelo y la de proyección.
1 2 3 4 5 6 7 8 9 10 11 12
// Pintamos el segundo billboard. // Este lo haremos extrayendo datos de // la matriz de preyeccion pasandoselos a // la matriz de modelo. Â // Ponemos el billboard encima de uno de los cubos. glTranslatef(-2,1, 4.0f); //Espacio para guardar la matriz de proyeccion. GLfloat py[16]; // Obtengo las matrices de modelo y proyeccion. glGetFloatv(GL_MODELVIEW_MATRIX, mv); glGetFloatv(GL_PROJECTION_MATRIX, py);
// Variables para los factores de correccion del // tamaño. Al usar los datos de la matriz de // proyeccion, la escala se modifica en base a la // perspectiva que hemos puesto. // Luego multiplicaremos estos factores por las coordenadas // al pintar el billboard. Si no el tamaño saldra mal al // estar no normalizados los vectores que extraemos de la // matriz de proyeccion. GLfloat z1,z2; // Dada esta perspectiva: // gluPerspective(60.0f, (float)rect.right/(float)rect.bottom, 0.5f, 50.0f); // El primer factor, si el primer parametro de la perspectiva es // 60. sera 0.60 (un poco menos queda mejor). // El segundo factor sera el segundo parametro de la perspectiva // por el primer factor. z2=1;//0.58f; // Ponemos 1 en caso de ser cilindrico. z1=((GLfloat)rect.right/(GLfloat)rect.bottom)*0.58f;
Copio los datos que necesito de la matriz de proyección a la de modelo transponiendo la submatriz de rotación de la matriz de proyección. (ver figura de matriz). Si es cilÃndrico en vez de el vector arriba extraido de la matriz de proyección, pongo 0,1,0.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// Cargo en la matriz de modelo los datos desde la matriz de // proyeccion (la parte del giro -y el escalado-) transponiendolos. // Las posiciones 4,5 y 6 de la matriz de modelo (igual que el caso // enterior) son para el vector de direccion hacia arriba. // Si uso el vector que indica el eje Y (0,1,0) sera de tipo cilindrico, // si uso los datos tomados de la matriz de proyeccion sera de tipo // esferico. // Vector derecha mv[0]=py[0]; mv[1]=py[4]; mv[2]=py[8]; // Vector arriba mv[4]=0;//py[1]; // Cilindrico mv[5]=1;//py[5]; // Cilindrico mv[6]=0;//py[9]; // Cilindrico // Vector direccion a la camara (al plano) mv[8]=py[2]; mv[9]=py[6]; mv[10]=py[10];
Si nos decidieramos a normalizar los vectores en vez de usar las variables "z1" y "z2", para mantener correcto el tamaño del cartel, lo hariamos aquÃ.
1 2 3 4 5 6
// En lugar de usar los factores z1 y z2 para recuperar el // tamaño normal del cartel, se puede normalizar los // vectores que extraemos de la matriz de proyeccion. //Normaliza(mv); //Normaliza(mv+4); //Normaliza(mv+8);
Cargamos la matriz de modelo hallada y pintamos como hicimos con el otro billboard.
// Cargo la matriz de modelo con los nuevos datos de giro. glLoadMatrixf(mv); // Y pinto el cuadrado del billboard (podria no ser un cuadrado) // Habilito textura glEnable(GL_TEXTURE_2D); // Hago bind de la que quiero usar glBindTexture(GL_TEXTURE_2D, tex4); // Indico como se aplicara glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); // Habilito blending si quiero. glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Y pinto el billboard. // Aqui aplico los factores de correccion del tamaño que // habiamos calculado antes sobre las coordenadas ya que // si no, se vera mas grande de lo que lo estamos pintando, // a no ser que hayamos normalizado los vectores, en cuyo // caso no haria falta. glBegin(GL_QUADS); glTexCoord2f(0.0,1.0); glVertex3f(-1, 0, 0); glTexCoord2f(1.0,1.0); glVertex3f(1*z1, 0, 0); glTexCoord2f(1.0,0.0); glVertex3f(1*z1, 2*z2, 0); glTexCoord2f(0.0,0.0); glVertex3f(-1, 2*z2, 0); glEnd();  // Dejo las cosas como debo. glDisable(GL_BLEND); glDisable(GL_TEXTURE_2D);
Una vez pintados los dos billboards solo quedan flecos.
Añado una entrada en la leyenda.
1 2 3
// Nuevas teclas en la leyenda Escribe(rect.right-300,rect.bottom-20, "Izquierda Derecha - Mover cámara en el eje X");
Nota: Las manipulaciones matematicas de las matrices y vectores que hemos visto en este capÃtulo, nos servirán para otras cosas como por ejemplo orientar un objeto, como un avión, en base a un vector de dirección.
¡Sólo los usuarios registrados pueden escribir comentarios!
Oye esta muy chido pero no me funciona
no se dedonde obtibiste las librerias que mencionas en el codigo final, soy nuevo en esto y me gustaria aprender
Vicengetorix
|89.130.162.xxx
|14-07-2013 17:19:17
En los capitulos 7 y 19 indica las librerias de graficos y de texto.
Conviene leer el curso en orden para no perderse estas cosas.
En la zona de descargas del sitio hay una cuantas tambien.