Você está na página 1de 40

OPENGL: TUTORIAL 0.

INTRODUCCIN GENERAL
Bueno, tratar de no ser pedante ni de liarme mucho. Programacin en 3D, engine, motor, aceleracin....muchos trminos que nos suenan. Todo se basa en un solo concepto: la representacin de figuras tridimensionales en la pantalla del ordenador. No quiero ser 'plasta', pero s es conveniente y necesario que comprendis que bajo todo esto existe una base matemtica muy compleja. Yo no soy matemtico, ni quiero serlo, as que en realidad no soy quien para explicar estas cosas. As que os tratar de explicar lo que yo he aprendido con mis propias palabras, sin ningn rigor matemtico (para eso estn los libros) y vale. Habr alguien que al leer 'matemticas' habr pensado algo como "qu coazo". Lo que quiero decir que aunque OpenGL nos apartar de todas estas cosas, es conveniente tener una nocin clara de qu estamos haciendo. Har falta tener algunos conocimientos matemticos bsicos y algo de imaginacin (visin espacial). El problema podra nombrarse como: Hallar la proyeccin de una serie de puntos sobre un plano. Esto es, la pantalla del ordenador es un plano. En realidad nosotros vemos las cosas ah en 2D. El truco es dibujarlas de tal forma que parezcan tener "fondo". Imaginad que este recuadro es vuestra pantalla.

Cualquier punto de la pantalla se puede representar con unas coordenadas (x,y). Esto es un plano. En un espacio tridimensional, existen 3 coordenadas: (x,y,z). En la pantalla falta la z. Ahora imaginaos un espacio virtual. Imaginaos que vuestra pantalla es un plano en ese espacio virtual:

Imaginad que la pantalla, de ahora en adelante denominada "cmara", mira al centro de ese espacio virtual. Ahora tenemos dos sistemas de coordenadas. Uno, el global, le llamaremos 'worldspace '. Es el grande de la figura. El otro sistema de coordenadas es el de la pantalla. Es el sistema asociado a 'cmara' . Echad ahora un vistazo a esta figura:

Eventualmente, tendremos un punto P con coordenadas P(x,y,z). Estas coordenadas son respecto al sistema 'worldspace'. Lo que tenemos que saber son esas coordenadas pero respecto al sistema 'camara'. A nosotros nos interesa siempre saber las coordenadas respecto a este sistema, ya que es el sistema fsico, nuestra pantalla. Una vez tengamos definido un mecanismo tal que podamos pasar de coordenadas del sistema 'worldspace' al sistema 'cmara', sabremos en cada momento dnde pintar se punto en la pantalla. Pero no acaba aqu la cosa. Pensad en una serie de puntos que define un objeto tridimensional. Si ese objeto se mueve por el espacio virtual, va a ser difcil recalcular en cada momento la nueva posicin de cada punto de ese objeto. Por ello, se define un nuevo sistema de coordenadas, el sistema 'objeto'.

Qu ventajas obtenemos?, si tenemos definido un objeto como una serie de puntos, definiremos estos puntos por coordenadas respecto al sistema 'objeto'. Estas coordenadas nunca cambian, lo que cambiar si queremos mover este objeto es la posicin y la orientacin de su sistema de coordenadas. Entonces, el proceso ser: a) Pasar las coordenadas de un punto del sistema 'objeto' al sistema 'worldspace' b) Pasar las coordenadas resultantes al sistema 'camara' c) Ya tenemos las coordenadas fsicas, y podremos dibujarlas en la pantalla. Y cmo se hace todo esto?. En el siguiente tutorial se ver una forma prctica de hacerlo que es usando matrices homogneas . Si no ests ducho en matrices, podras pensar en pegarle un repasillo. De todas maneras recuerda que esto es una introduccin y lo ms importante es quedarse con el concepto y comprender, que saber operar con matrices. Ante cualquier duda, mandadme un e-mail (No digo emilio que luego los Emilios se me mosquean).

OPENGL: TUTORIAL 1. MATRICES Y ASPECTOS MATEMTICOS

En el tutorial 0 vimos una breve introduccin de cmo se utilizaban 3 sistemas de coordenadas ( cmara, worldspace y objeto) para facilitar la conversin de coordenadas en el espacio virtual. Vamos a ver ahora implementaciones de esta teora. Primero vamos a definir lo que es un vector :

Si le echamos un vistazo a la figura vemos un sistema de coordenadas y un vector V. Un vector bsicamente es una flecha, y nos indica una direccin. Matemticamente un vector en el espacio tridimensional se expresa con 3 coordenadas (x,y,z). Notad que la flecha expresa el sentido positivo del vector. Hay que tener muy claro un par de cosas. El vector tiene mdulo (digamos que es su tamao), direccin y sentido. Ahora echad un vistazo a esta otra figura:

Todos esos vectores son el mismo vector. Si, estn en sitios diferentes, pero todos indican la misma direccin y sentido. Matemticamente son vectores iguales. Eso s, notad en la figura de la derecha, que los dos vectores dibujados NO son iguales. Tienen la misma direccin pero ved que uno es ms largo que el otro (Tambin es ms grueso, pero eso son cuestiones de pifias mas al dibujar, el grosor no tiene nada que ver). Al ser uno ms largo que el otro, eso les hace ser diferentes. Matemticamente se dira que sus mdulos son distintos). Llegamos pues entonces a la conclusin de que un vector es una direccin en el espacio .

Hemos dicho que un vector se representa por tres coordenadas (x,y,z) en el espacio tridimensional. Un punto tambin se representa por tres coordenadas en el espacio tridimensional (x,y,z). Sin embargo un punto es algo totalmente diferente, ya que representa pues eso, un punto en el espacio. Bueno, ahora toca hablar un poco de los sistemas de coordenadas o referencia. Un sistema de coordenadas , referencia o base, es una referencia para establecer las coordenadas.

Imaginemos que eso es un paisaje. El punto P viene representado por 3 coordenadas (x,y,z), pero estas coordenadas sern distintas dependiendo del sistema de referencia que tomemos En realidad un sistema de referencia son 3 vectores. Fijaos en la figura. Los sistemas de referencia que utilizaremos van a ser orto normal . Qu significa esto?. Esto significa 2 cosas. Los vectores van a formar siempre un ngulo de 90 grados entre ellos. Esto implica que el producto escalar entre los vectores es 0. Y adems los vectores son unitarios (su mdulo es uno). A la prolongacin del vector hacia el infinito se le llama eje. Producto escalar : si tenemos un vector V (x1,y1,z1) y otro U (x2,y2,z2), el producto escalar es un nmero que se calcula as: (x1*x2+y1*y2+z1*z2) El mdulo de un vector expresa (patateramente dicho) su longitud, su tamao. Se calcula dado V (x1,y1,z1) como la raz cuadrada de x1*x1+y1*y1+z1*z1. Escrito ms bonito: .

Si vemos estas dos imgenes, nos damos cuenta de que el segundo sistema es una autntica mierda. Los vectores que lo forma ni son de igual longitud ni forman ngulo de 90 grados. El primero est bien (bueno, el eje z es como si se metiese en la pantalla y no se vera, pero de alguna forma haba que representarlo). Bueno, una vez dicho esto antes de definir por completo los sistemas de coordenadas, vamos a comentar un pequeo truco que nos ayudar en todo esto. Hemos dicho que un punto y un vector se representan con 3 coordenadas. Vamos a aadirle una cuarta

en determinadas circunstancias para diferenciarlos. Un punto ser de la forma (x1,y1,z1,1) y un vector de la forma (x1,y1,z1,0). Esto implica que: 1. Un punto ms un vector es otro punto: (x1,y1,z1,1)+(x2,y2,z2,0)=(x1+x2,y1+y2,z1+z2,1). Esto representa un punto al cual se le suma un vector (o sea, un desplazamiento en una direccin y un sentido) y sale otro punto. Por ejemplo. Si estis en un punto y avanzis en la direccin, sentido del vector, acabis en otro punto. 2. Un punto menos un punto es un vector : (x1,y1,z1,1)-(x2,y2,z2,1)=(x1-x2,y1-y2,z1-z2,0). El vector resultante se puede interpretar como el desplazamiento que hay que hacer para pasar del punto 1 al punto 2 3. 4. La resta de un vector menos un punto no tiene sentido La suma, resta y ms operaciones de vectores se puede realizar ignorando la cuarta coordenada.

Las coordenadas (x,y,z) respecto a un sistema de referencia expresan cunto debemos desplazarnos a lo largo de los ejes x y z para llegar al punto que estamos referenciando. Pues bien, ya estamos en posicin de definir nuestros sistemas de coordenadas. En primer lugar tenemos el sistema de coordenadas cannico (base cannica). Es el formado por los vectores: x=(1,0,0) y=(0,1,0) z=(0,0,1). Nuestro sistema de referencia principal, el sistema worldspace, tiene esta base y siempre permanece inalterada. Todas las coordenadas del mundo virtual estn dadas respecto a esta base. Es como el kilmetro 0. Pero hay algo ms, mirad esta figura:

Tenemos aqu dos sistemas. Segn los hemos definido hasta ahora son dos sistemas iguales (suponiendo que todos los vectores tienen el mismo mdulo). Sin embargo vemos que NO estn ubicados en el mismo punto. Por ello, y definitivamente, vamos a definir un sistema de referencia o base como una matriz as: a a' a'' x b b' b'' y c c' c'' z

0 0 0
Esta matriz se interpreta as: 1.

Las 3 primeras columnas representan los vectores que forma la base. x=(a,b,c,0), y=(a',b',c',0) z=(a'',b'',c'',0). Fijaros que el ltimo elemento es un 0, lo que indica que es un vector. La ltima columna indica el desplazamiento. Esto es un punto (x,y,z,1) (termina en un 1 para indicar que es un punto) y es el origen del sistema de coordenadas.

2.

Por ejemplo, la matriz de la base cannica es:

1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1
Para los que sepan manejar matrices, vern que es la matriz identidad. Es recomendable a partir de aqu que repasis en cualquier libro de texto el manejo de matrices. Sobre todo la multiplicacin de matrices. Bien, a partir de ahora: TODOS LOS SISTEMAS SE REPRESENTARN CON MATRICES HOMOGNEAS . Echemos un vistazo a esta figura:

Tenemos 3 sistemas (worldspace, camara, y objeto). Fcilmente vemos que las matrices que representan estos 3 sistemas son iguales diferencindose slo en la cuarta columna, el desplazamiento. Por qu?, porque los vectores que forman las bases son iguales y slo se diferencian en el origen. Estas podran ser las matrices de los sistemas:

worldspace

objeto

camara

1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1

1 0 0

1 0 0 5 0 1 0 3 0 0 1 0 0 0 0 1

0 1 0 5 0 0 1 0 0 0 0 1

Bien, estas matrices que representan los sistemas son homogneas. El caso es que tienen unas propiedades que nos sern muy tiles: Usualmente, tendremos un punto P perteneciente a un objeto, cuyas coordenadas (x,y,z) conocemos. Estas coordenadas son respecto al sist. 'objeto'. Queremos saber las coordenadas respecto al sistema 'worldspace'. Bien, pues simplemente multiplicamos la matriz asociada al sist. 'objeto', por las coordenadas del punto P respecto a ese sistema: El resultado son las coordenadas de ese punto respecto al espacio virtual.

objeto

P(respecto worldspace)

1 0 0 5 0 1 0 3 x 0 0 1 0 0 0 0 1

2 1 = 0 1

7 4 0 1

Hemos supuesto que el origen del sist. 'objeto' es (5,3,0) y las coord. de P respecto a 'objeto' (2,1,0). La conclusin de todo esto es que veis que la matriz homognea nos permite pasar de unos sistemas a otros muy fcilmente. Podemos denominar a la matriz asociada al sist. 'objeto' como A(O->W), porque pasa coordenadas del sistema 'objeto' al sistema 'worldspace'. Anlogamente, la matriz asociada a 'cmara' pasa coordenadas del sist. 'cmara' al sist. worldspace. Pero eso no es lo que nos interesa. Nos interesa pasar del sist. 'worldspace' al sistema 'camara'. Si a la matriz asociada a 'camara' B(C->W) le hallamos su inversa, tenemos la matriz B'. Esta matriz inversa a B hace exactamente lo contrario. B'(W->C) pasa coordenadas del sistema 'worldspace' al sistema 'camara' Y ESO ES LO QUE NOS INTERESA. Para cualquier punto P definido en un sistema O, dadas sus coordenadas respecto a ese sistema, sus coordenadas respecto al sistema 'cmara' sern: P(respecto a 'camara')=B' x (A x P) Hay que tener en cuenta que: 1. 2. Cada vez que la cmara cambia su posicin u orientacin, B cambia y por tanto B' cambia Cada vez que el objeto O cambia su posicin u orientacin, A cambia.

Bueno...y este es el final de este tutorial. Solo resta decir que OpenGL nos ahorrar en realidad la mayora de las operaciones intermedias, pero es bueno decir que para el desarrollo de un motor 3D, siempre se utilizaran estas matrices para definir los objetos en cada momento. Es bueno saber manejar estas matrices para hacer rotaciones, desplazamientos y muchas cosas ms, pero ya digo que no os asustis porque OpenGL nos ahorrar muchsimo trabajo y complicaciones. Si tenis dudas, el tutorial os parece demasiado complicado, lo que sea, no os lo pensis y escribidme a q3arena@ciudadfutura.com. Tambin agradecer vuestros comentarios.

OPENGL: TUTORIAL 2. COMENZANDO CON OPENGL

Este tutorial es uno de los ms importantes de los que se hayan escrito hasta ahora. Vamos a empezar a programar en C y OpenGL. Por lo tanto, supondremos que ya tenis vuestro compilador listo y preparado para empezar. (Si no echad un vistazo al tutorial 2.0). En primer lugar, tenemos que decir que usaremos programacin modular. Para aquellos que no les suene todava demasiado el C, es simplemente que dividiremos el cdigo fuente en varios ficheros. Lo haremos as para organizarnos mejor y saber donde est cada cosa. Empecemos: Bajaos este fichero que es el cdigo fuente de este tutorial ( tut_1_2.zip ). Contiene varios ficheros, echmosles un vistacillo:

estructuras.h En este fichero se definen estructuras nuevas que utilizaremos. Como es un fichero que se incluir desde muchos ficheros fuente, se "defiende" con el comando de preprocesador ifndef. Esto lo que hace es que si el compilador ya ha ledo estas estructuras, no las vuelva a leer. #ifndef _ESTRUCTOK #define _ESTRUCTOK typedef struct config_tag { short int SCREEN_SIZE_X; short int SCREEN_SIZE_Y; char FULLSCREEN; } configuracion; #endif Esta estructura se utiliza para almacenar la configuracin con la que vamos a ejecutar la aplicacin. init_sys.c init_sys.h En estos ficheros se define una funcin: system_init(). En este ejemplo lo nico que hace es rellenar la variable de configuracin config. En ejemplos posteriores, se utilizar para inicializar el sistema en general. En init_sys.c viene definida la funcin y la variable global config, y en init_sys.h viene el prototipo de la funcin y la variable global como extern. init_sys.c #include "estructuras.h" /* Variable global para almacenar la configuracin */ configuracion config; / ----------------Inicializacin del sistema ----------------*/ int system_init(void){ config.SCREEN_SIZE_X=640; config.SCREEN_SIZE_Y=480; config.FULLSCREEN=1; return 0; } init_sys.h #include "estructuras.h" /* Funciones */ extern int system_init(void); /* Inicializacin del sistema */ /* Variables */ extern configuracion config; /* Variable para almacenar la configuracin */

sys.h sys_linux.c sys_win32.c Estos ficheros son demasiado largos como para incluirlos aqu. Pero es conveniente explicar que: 1. sys_linux.c y sys_win32.c implementan las mismas funciones: Por qu?. Al principio de estos ficheros veremos las directivas#ifdef _LINUX y #ifdef _WIN32 respectivamente. Esto lo que hace es que el compilador de Windows compila el fichero sys_win32.c pero ignora el fichero sys_linux.c , y el de Linux hace todo lo contrario, compila sys_linux.c e ignora sys_win32.c. Esto se hace porque en estos ficheros vienen implementadas las funciones DEPENDIENTES DEL SISTEMA . Esto quiere decir, las partes de cdigo que difieren entre Linux y Windows. sys.h es comn. Es un fichero con los prototipos de las funciones que necesitan ser utilizadas por otros mdulos. El punto de entrada de la aplicacin, o sea, el main() el inicio del programa est en los ficheros sys_linux.c y sys_win32.c.

2.

3.

4.

Por lo general, los dos ficheros sys_*.c, harn lo mismo, pero cada uno para su plataforma. El de windows abrir una ventana y le asignar un contexto OpenGL, y llamar a la funcin init_gl() (ver ms adelante) entre otras cosas y entrar en un bucle infinito que llamar constantemente a la funcin gl_renderiza_escena() (ver ms adelante) y chequear si se pulsa escape. El de Linux har lo mismo pero c en vez de utilizar el API de Windows, utilizar una librera para Linux que se llama SDL de la cual hablaremos ms adelante. Las funciones que se exportan a otros mdulos desde estos dos ficheros son dos: extern void sys_swapbuffers(void); Intercambio de buffers. El intercambio de buffers (ver doublebuffering ms adelante) es dependiente del sistema, por eso est implementada aqu, pero se llama desde otros modulos, por eso se exporta. extern void sys_msg(char *msg); Cualquier otro modulo puede utilizar esta funcin para dar un mensaje de error. En Windows se abrir un dilogo mientras que en Linux se escribir en la salida estndar. render.c y render.h Estos ficheros son la clave. Son comunes y los veris en la siguiente pgina a fondo y ms explicados. La programacin OpenGL est ah. Exportan dos funciones extern void gl_renderiza_escena(void); Funcin ncleo. Aqu se renderizar toda la escena extern int init_gl(void); Inicializacin propia de OpenGL opengl.h A la hora de incluir las cabeceras OpenGL, Windows y Linux difieren un poco. Por ello, en vez de incluir las cabeceras estndar de OpenGL, incluimos este fichero que las incluye correctamente dependiendo del sistema (Linux o Windows) utilizando el truco #ifndef. #ifdef _WIN32 #include <windows.h>

#include <gl/gl.h> #include <gl/glaux.h> #endif #ifdef _LINUX #include <GL/gl.h> #include <GL/glu.h> #endif Bueno. Antes de empezar con OpenGL propiamente dicho, veamos un esquema de toda la aplicacin. Cada mdulo se representa como una caja que lleva arriba las funciones que exporta ( render.c exporta sus funciones por medio de render.h ). sys_win32.c y sys_linux.c comparten sys.h

Como norma general todas las variables globales se declaran en init_sys.c y se exportan con init_sys.h. El mdulo que quiera utilizar una variable global o una funcin que est en otro mdulo debe incluir (#include) el fichero de cabecera. Por ejemplo, render.c utiliza la funcin sys_swapbuffers() de sys_linux.c por ello incluye el fichero sys.h con la directiva include. Algunos pensarn que todo esto es un poco lioso para un ejemplo tan sencillo, pero se hace as por una buena razn. Todos el cdigo de los tutoriales seguir esta estructura, y el engine 3D probablemente tambin. Cuando las aplicaciones sean ms grandes ser muy til tenerlo todo organizado de esta manera. Pero an hay ms....

Bien, ya tenemos claro la organizacin de ficheros. Ahora vamos a explicar lo que hay en fichero render.c que es donde se realiza el renderizado. Tenemos dos funciones. Primero vamos a ver la funcin init_gl(). Con esta funcin inicializaremos OpenGL debidamente.

/* -----------init_gl Inicializacin propia de OpenGL -----------*/ int init_gl(void) { Este cdigo es un poco complejo...glMatrixMode sirve para indicar que vamos a modificar la matriz de proyeccin o la de modelado. No os comis demasiado la cabeza con esto.glLoadIndentity() la resetea. Con gluPerspective lo que hacemos es indicar a OpenGL que vamos a utilizar perspectiva.Esto hace que a medida que las cosas estn ms lejos se van haciendo ms pequeas y tienden al centro. Esta perspectivahar que lo que veamos sea ms realista. Adems, los dos ltimos parmetros sirven para indicar que el rango de visivilidadva desde .1 a 100. Es decir, lo que est situado a ms de 100 unidades de la cmara no se ver. Podis cambiar estos parmetros y observar los cambios. Es la mejor manera de aprender y comprender. glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0f,(GLfloat) config.SCREEN_SIZE_X /(GLfloat) config.SCREEN_SIZE_X, 0.1f, 100.0f); /* Esto resetea la matriz de modelado. Es la forma de resetear en OpenGL */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); /* Ahora establecemos el color de fondo por defecto. Est en formato RGBA y lo ponemos a negro */ glClearColor(0.0f, 0.0f, 0.0f, 0.0f); /* Estas 3 lneas habilitan del Buffer de profundidad*/ glClearDepth(1.0f); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); /* Esta funcin sirve para optimizar el renderizado, y se utiliza para muchas cosas. En este caso es parahacer un buen clculo de la perspectiva perdiendo un poco de rendimiento*/ glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); return 0; }

Notas: 1. Como OpenGL trabaja con matrices internamente, la forma de resetear es tal como veis. con glLoadIndentity . Formato RGBA. Un color se expresa con 4 bytes. El primero es el componente Rojo, el segundo el componente Verde y el tercero el componente Azul (Red-Green-Blue). Hasta aqu es formato

2.

RGB. El RGBA incluye otro byte ms que se suele llamar Alpha, e indica la transparencia. Ya trabajaremos ms sobre esto. 3. Buffer de profundidad (DEPTH BUFFER). El buffer de profundidad se utiliza para evitar que los objetos que estn detrs se dibujen delante de los que los tapan. Por cada pixel dibujado en la pantalla, se almacena su coordenada Z. Si posteriormente se va a dibujar un punto en esa misma posicin, se comprueba si el que est ya dibujado va delante o detrs. La funcin glHint se utiliza para muchas ms cosas. Con ella podemos jugar entre calidad y rendimiento. Notad que todas las funciones de OpenGL empiezan por gl. Las que empiezan por glu, son funciones de GLU (GL Utility), que es una librera de apoyo a OpenGL bsico. Suele venir includa.

4.

5.

/* ----------gl_renderiza_escena Aqu se hacer la renderizacin ----------*/ void gl_renderiza_escena(void){ static float angulo=0.0; /* Reseteamos los buffers y la matriz de modelado */ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); /* Trasladamos la base 6 unidades hacia dentro de la pantalla*/ glTranslatef(0.0f,0.0f,-6.0f); /* Rotamos alrededor del eje Y un ngulo=angulo */ glRotatef(angulo,0.0f,1.0f,0.0f); /* Comenzamos a dibujar el tringulo. Cada 3 vrtices indican un tringulo*/ glBegin(GL_TRIANGLES); glColor3f(1.0f,0.0f,0.0f); /* Color del vrtice */ glVertex3f( 0.0f, 1.0f, 0.0f); /* Vrtice superior */ glColor3f(0.5f,0.5f,0.0f); /* Color del vrtice */ glVertex3f(-1.0f,-1.0f, 0.0f); /* Vrtice de nuestra izquierda*/ glColor3f(0.0f,0.0f,0.2f); /* Color del vrtice */ glVertex3f( 1.0f,-1.0f, 0.0f); /* Vrtice de nuestra derecha */ glEnd(); angulo=angulo+0.5f; /* Incrementamos el ngulo de giro */

sys_swapbuffers(); /* Llamamos a la funcin de intercambio de buffers*/ } Comentarios: Cuando entramos en esta funcin, debemos imaginar que estamos situados en el centro del espacio virtual. Los ejes de coordenadas sera estos:

Es ms, para que lo veis mejor, es como si el centro del espacio estuviera en el centro de la pantalla:

El eje Z saldra de la pantalla hacia nosotros. En este ejemplo vamos a dibujar un tringulo que rota. Por defecto se comienza a dibujar respecto al centro. Entonces, lo que hacemos es trasladarnos 6 unidades hacia dentro de la pantalla. Esto se hace con glTransalatef. Indicamos que queremos movernos 6 unidades en el sentido negativo del eje Z (0.0,0.0,-6.0). El sentido positivo es el que indican las flechas, (el del eje Z es hacia nosotros). Con esto conseguimos que ahora empezamos a dibujar desde ese punto. Lo siguiente que hacemos es rotar. Utilizamos una variable esttica (angulo). Esta variable valdr 0 la primera vez que se utilice y conservar su valor a pesar de que salgamos de la funcin y entremos en ella varias veces. Eso s, no es accesible desde otras funciones. Con glRotatef indicamos que giraremos un angulo sobre el eje Y (0.0,1.0,0.0). Notas que todas las coordenadas se dan con notacin x,y,z. (0.0,1.0,0.0) indica el vector del eje Y. El giro es positivo. Para saber el sentido de giro que queremos darle, utilizamos la regla del sacacorchos. Poned el dedo ndice (mano derecha) como el eje Y apuntando hacia donde indica la flecha (en este caso hacia arriba). Imaginad ahora que el dedo es un tornillo que queris apretar. Pues el sentido de giro que hacis para apretar es el positivo. O sea, si ponemos en la variable angulo un ngulo positivo, el giro se efecta hacia el sentido que se hara para apretar un tornillo. Recodad glRotatef. Rota sobre el eje Y un ngulo (variable angulo) Bien, ahora, con la sentencia glBegin indicamos que comenzamos a dibujar. El parmetro GL_TRIANGLES har que cada tres puntos se dibuje un tringulo cerrado. Si pusisemos GL_LINES, se dibujara una lnea cada dos puntos dados. Hay ms opciones ( GL_BOXE, GL_POLYGONS..). Sin embargo, dibujar tringulos es el mtodo ms adecuado y el que se sigue ms en el mundo 3D.

Bien, entre glBegin y glEnd se dibuja el tringulo. Las funciones son glColor3f, que indica el color de ese vrtice en formato RGB, y glVertex3f que lo posiciona en el espacio. Recuerda que las coordenadas que se dan son respecto a la base que dejamos lista con glTransalef y glRotatef. Esto quiere decir, que dependiendo de las guarradas que hagamos con la base antes de dibujar, los puntos se pintan en un lugar u otro. Por ltimo llamamos a la funcin de intercambio de buffers. Nosotros utilizamos Doublebuffering . Esto es un sistema (para quellos que no lo hayan utilizado ya) para evitar parpadeos en las animaciones. Imaginad que queremos mover un muequito por la pantalla. Lo ponemos en una posicin, lo movemos y lo dibujamos en otra. Pero hay que borrar el anterior. Entre el intervalo de tiempo entre que se borra y se vuelve a dibujar se produce un parpadeo. Para ello, con este sistema lo que se hace es no dibujar en pantalla directamente. Dibujamos en un buffer de memoria y cuando una imagen ya est lista (un frame ya est renderizado), se pasa a la pantalla sobreescribiendo el anterior. As siempre vemos frames acabados. Pues nada, aqu se acaba este tutorial. Ya sabis escribid por si hay dudas preguntas o sugerencias.

OPENGL: TUTORIAL 3. TEXTURAS.

En este tutorial aprenderemos a: 1. 2. Cargar una textura de un fichero grfico TGA Dibujar un objeto slido (un cubo) y aplicarle la textura cargada

El cdigo de ste tutorial se basa sobre el anterior, as que slo comentaremos las partes nuevas que se ha aadido. El cdigo fuente est aqu. En primer lugar, vamos a comentar algo sobre el formato de color que utilizaremos. Si nos fijamos en el tutorial 1.2, cuando ponamos un vrtice del tringulo, antes de posicionarlo en s ponamos este comando: glColor3f(1.0f,0.0f,0.0f); /* Color del vrtice */ Con esto especificbamos el color del vrtice del tringulo a pintar. El color est expresado en esta funcin con tres componentes. Este formato se llama RGB (Red-Green-Blue). Bien, nosotros vamos a utilizar el formato RGBA, que es lo mismo, pero se aade un byte ms, que se llama canal Alfa (RedGreen-Blue-Alpha). Este canal servir ms adelante para especificar el color ms una componente de transparencia. El canal alfa servir para decir "cunto" de transparente ser ese color. En este tutorial, de todas maneras, no haremos uso de ello. En formato RGBA, la funcin anterior sera: glColor4f(1.0f,0.0f,0.0f,0.0f); /* Color del vrtice */ El caso es que el formato TGA es un formato que usa varias variantes, y una de ellas es el formato RGBA, que es el que nosotros uilizaremos.

Para ello, tenemos en el tutorial un par de ficheros nuevos (respecto al anterior). Estos son carga_tga.c y su cabecera. carga_tga.c tiene una funcin que lo que har es cargar en memoria un fichero TGA. El fichero en memoria ocupar un espacio que podemos calcular as: ancho * alto * 4 bytes. Se multiplica por 4 porque es el peso del formato RGBA. (Tb se conoce como 32 bits). He aqu la funcin: typedef struct // Utilizamos esta estructura { GLubyte *imageData; GLuint bpp; GLuint width; GLuint height; GLuint texID; } Imagen; /* --------------------Carga_TGA Carga un TGA en memoria. El TGA debe cumplir las siguientes caractersticas: Ser de 24 bits + Canal ALPHA. (32 bits) y SIN COMPRIMIR El tamao debe ser cuadrado (x=y) y 32x32 o 64x64 o 128x128 o 256x256 Devuleve un puntero a la imagen y el tamao (variable tam) de la imagen ----------------------*/ void *CargaTGA(char *filename,int *tam) { GLubyte TGAheader[12]={0,0,2,0,0,0,0,0,0,0,0,0}; GLubyte TGAcompare[12]; GLubyte header[6]; GLuint bytesPerPixel; GLuint imageSize; GLuint temp,i; GLuint type=GL_RGBA; Imagen texture; GLubyte *aux; FILE *file = fopen(filename, "rb"); if (file == NULL) return NULL; /* Esto abre y comprueba que es un TGA */ fread(TGAcompare,1,sizeof(TGAcompare),file); if (memcmp(TGAheader,TGAcompare,sizeof(TGAheader))!=0) return NULL; /* Leemos la cabecera*/ fread(header,1,sizeof(header),file); /* Determinamos el tamao */ texture.width = header[1] * 256 + header[0]; texture.height = header[3] * 256 + header[2];

/* Vemos las caractersticas y comprobamos si son correctas*/ if( texture.width <=0 ||texture.height <=0 ||texture.width >256 ||texture.height !=texture.width ||( header[4]!=32)) { fclose(file); return NULL; } /* Calculamos la memoria que ser necesaria */ texture.bpp = header[4]; bytesPerPixel = texture.bpp/8; imageSize = texture.width*texture.height*bytesPerPixel; /* Reservamos memoria */ texture.imageData=(GLubyte *)malloc(imageSize); /* Cargamos y hacemos alguna comprobaciones */ if( texture.imageData==NULL || fread(texture.imageData, 1, imageSize, file)!=imageSize) { if(texture.imageData!=NULL) free(texture.imageData); fclose(file); return NULL; }

/* El TGA viene en formato BGR, lo pasamos a RGB */ for(i=0; i<(GLuint)(imageSize); i+=bytesPerPixel) { temp=texture.imageData[i]; texture.imageData[i] = texture.imageData[i + 2]; texture.imageData[i + 2] = temp; } fclose (file); /* Ahora, cambiamos el orden de las lneas, como si hiciesemosun flip vertical. */ aux=(GLubyte *)malloc(imageSize); for(i=0; i<texture.height; i++) memcpy(&aux[imageSize((i+1)*texture.width*4)],&texture.imageData[i*texture.width*4],texture.width*4); /* tam devolver el tamao */ *tam=texture.width; /* Liberamos memoria */ free(texture.imageData); /* Todo fue bien!*/ return aux; }

Bueno, vista la funcin, lo que vamos a tener ahora despus de llamarla es un espacio en memoria conteniendo esa imagen. En la variable tam se nos devolver el tamao de la imagen. Unos apuntes ahora: 1. 2. 3. El tamao ha de ser cuadrado. Esto es una restriccin de OpenGL. Tiene que ser potencia de 2. (2*2, 2*2*2, 2*2*2*2,..) o sea , 2,4,8,16,32,64,126,256,... Utilizamos un mximo de 256x256. En algunas implementaciones de OpenGL puede ser ms, pero con objeto de tener un cdigo portable aceptamos esto. En caso de tener una imagen que no cumpla todo esto, la funcin devolver error.

4.

Bueno. Ya tenemos el primer paso hecho. Ahora vamos a ver otra funcin nueva en el fichero render.c /* ---------------carga_texturas Cargamos texturas --------------*/ int carga_texturas(void) { int tam; textura_datos=(char *)CargaTGA("textura.tga",&tam);if (textura_datos == NULL) { return -1; } /* Genera una textura, referenciada por el entero textura */ glGenTextures (1, &textura); /* Esta funcin indica que ser una textura en 2D. Las siguientes funciones har referncia a esta textura */ glBindTexture (GL_TEXTURE_2D, textura); /* Aqu ajustamos algunos parmetros de la textura, concretamente los filtros */ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST); /* Con esta funcin de GLU, creamos un mipmap. Ntese que especificamos el tamao con tam, que devolvia la funcin CargaTGA*/ gluBuild2DMipmaps (GL_TEXTURE_2D, 4, tam, tam, GL_RGBA,GL_UNSIGNED_BYTE, textura_datos); /* Liberamos la memoria que ya no utilizaremos */ free(textura_datos); return 0; } Una vez tenemos la imagen cargada en memoria, los pasos a seguir son:

1.

Generar la referencia para la textura. [glGenTextures (1, &textura);] Con el GLuint textura referenciaremos a "esa textura". Si quisieramos tener ms, deberamos crear un array de GLuint. El 1 significa que slo generamos una textura. Referenciamos esa textura: [glBindTexture (GL_TEXTURE_2D, textura);]. Esta funcin dice que ahora en adelante, todas las operaciones que afecten al manejo de texturas se aplicarn sobre esa textura. Es una textura 2D. Especificamos los filtros para esa textura: (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);

2.

3.

glTexParameteri glTexParameteri

OpenGL puede aplicar filtros a las texturas para mejorar su visualizacin. La primer lnea especifica el filtro a utilizar en caso de que las textura se vea ms grande de lo que en realidad es (Aumento). La segunda, especifica el filtro para el caso de que la textura se vea ms pequea de lo que en realidad es. Aqu utilizamos un filtro LINEAR para el 1er caso y del segundo hablaremos ms adelante. Otro valor que podramos haber utilizado es el GL_NEAREST, que es un filtro peor pero ms rpido 4. Creamos la textura: En este caso nosotros utilizaremos MIPMAPS. Qu es un MIPMAP?, para mejor rendimiento, en vez de filtrar las textura dinmicamente, al crear un mipmap, creamos varias texturas de diferentes tamaos (del original hacia ms pequeo). OpenGL, decidir en funcin de lo lejano que est el objeto con esa textura, de qu mipmap utilizar. gluBuild2DMipmaps (GL_TEXTURE_2D, 4, tam, tam, GL_RGBA,GL_UNSIGNED_BYTE, textura_datos); Podramos haber escogido la forma tradicional que es la siguiente: glTexImage2D(GL_TEXTURE_2D, 0, 3, tam, tam, 0, GL_RGB, GL_UNSIGNED_BYTE, textura_datos); En este caso, generaramos una textura simple (En el segundo filtro habra que cambiar el ltimo valor). 5. Ya una vez aqu, slo nos hace falta habilitar el empleo de texturas con: glEnable(GL_TEXTURE_2D); En la funcin init_gl.

Bien, ya slo nos queda mapear la textura en el objeto a dibujar. Ya para terminar, nos queda el mapeo de texturas. Lo que haremos es echar un vistazo en la funcin gl_renderiza_escena: /* Con esta funcin indicamos que vamos a utilizar la textura */ glBindTexture(GL_TEXTURE_2D, textura); /* Comenzamos a dibujar el cubo. Cada 4 vrtices indican una cara*/ /* Con la funcion glTexCoord2f mapeamos las coordenadas de la textura*/ glBegin(GL_QUADS); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, -1.0f, 1.0f);

glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glTexCoord2f(0.0f, 1.0f);.................... ..... 1. 2. Lo primero es decirle a OpenGL qu textura vamos a utilizar (glBindTexture....) Dibujamos con GL_QUADS. Sera lo mismo con GL_TRIANGLES. Vamos a dibujar un cubo, cada 4 vrtices son una cara del cubo. Antes de definir cada vrtice, especificamos que punto de la textura utilizamos. El meollo est aqu. Con glTexCoord2f decimos que en el vrtice x,y,x estar mapeada la coordenada u,v de la textura. Las coordenadas u,v van de 0 a 1 y especifican : 0.0 -> Es el origen (esquina sup izq). 1,0-> Es la esquina sup derecha. 1,1-> Es la esquina inferior derecha. 0,1-> Es la esquina inferior izq. La ventaja de esta notacin es que es independiente del tamao de la textura. As que trataremos por igual todas las texturas

3.

Bueno, antes de terminar, un par de notas: - Tal vez queramos utilizar una pequea textura para rellenar una cara del cubo. Ahora mismo, se cogera esa textura y se estirara para rellenar la cara. Si ponemos en las coordenadas u,v (2.0f,2.0f) estaramos indicando que en vez de estirar las textura, se aplicara un mapeo utilizando la textura 2 veces: A ver si me explico mejor con estos dos screenshots:

Respecto a la funcin que carga TGA's, decir que es un poco tosca. Se mejorar en el futuro. El formato, os recuerdo, debe ser 24 bits+Canal ALPHA, SIN COMPRESIN (TGA se puede comprimir con RLE) y de tamao: 2x2,4x4,8x8,16x16,32x32,64x64,128x128,256x256. Bueno, aqu se acaba este tutorial, espero que os haya servido y espero vuestros comentarios.

OPENGL: TUTORIAL 4. ALGUNAS CUESTIONES SOBRE EL MODELADO


Hasta ahora, cada vez que tenamos que dibujar algo, utilizbamos la funcin glVertex a mano. Es decir, hemos pintado un cubo, un tringulo, y todo ello a mano. Obviamente, lo ms razonable es crear un modelo con algn programa dedicado a ello (por ejemplo el 3D Studio) y luego utilizarlo en nuestro motor. De ello va este tutorial, y el objetivo es: - Conocer algunas de las cuestiones bsicas del modelado - Cargar modelos desde ficheros externos Apartado 1: BACK-FACE CULLING Hasta ahora hemos dibujado objetos bien con tringulos o con cuadrados. Sea como sea, la unidad fundamental de todo ello ha sido la "cara" (ing. 'face'). De ahora en adelante, cada vez que dibujemos un modelo, lo haremos mediante tringulos. Para nosotros una cara es una coleccin de tres vrtices.

Bien. Los modelos los dibujaremos basndonos en una cara. Si os dis cuenta, el trmino cara implica una cosa. En realidad un tringulo son dos caras, pero se suele utilizar una sola en el modelado. El ejemplo es el siguiente: En el tutorial anterior dibujbamos un cubo a partir de cuadrados. Sin en el cdigo quitsemos una cara, por ejemplo la primera, veramos el interior del cubo. Bien, esto puede no ser deseable. Por qu?, si la situacin normal es un cubo cerrado, OpenGL estara dibujando las caras que quedan en el interior del cubo, PERO NOSOTROS NO LAS VEMOS por que el cubo es cerrado. (no tendramos que meter en el interior para verlas). Con estas dos funciones: glCullFace(GL_BACK); glEnable(GL_CULL_FACE); le estamos diciendo a OpenGL que NO dibuje las caras traseras. En este cdigo ( tut_1_4_1.zip) le hemos quitado una cara y hemos activado el BFCulling.

Notemos que la cara que hemos volteado no se ve, las caras del interior del cubo tampoco se ven!!!. Esta tcnica puede acelerar mucho el rendimiento de un motor , ya que OpenGL tratar la mitad de caras. Pero, ahora bien, cual es la forma de decirle a OpenGL que cara es la delantera?. Se sigue un estndar referente al orden de los vrtices. Echad un vistazo a esta figura:

Si definimos esa cara como ABC, el sentido de giro ser como las agujas del reloj. Si seguimos la regla del sacacorchos-tornillo-manoderecha, la cara frontal queda hacia dentro de la pantalla. Si definimos la cara como CBA, ocurre al contrario, la cara frontal es la que nosotros vemos.

Apartado 2: NORMALES El concepto de normal en OpenGL, es como el concepto de normal en geometra. La normal nos va a indicar la cara frontal de un polgono (una cara). En cierto modo es lo mismo que nos indica el orden de los vrtices vistos anteriormente. Las normales van a servir para que OpenGL haga bien la iluminacin. Esto no es un tutorial de iluminacin, pero en el cdigo nmero dos (tut_1_4_2.zip) la vamos a utilizar. Entonces la normales, tienen mucho que ver con la iluminacin. El objetivo de explicarlo aqu es que en el apartado posterior tendremos un par de referencias. La normal es un vector (3 coordenadas) que van a expresar la cara frontal y es perpendicular al plano en el que est contenido la cara. Si hemos estudiado matemticas, hemos visto que un plano se sola definir como: Ax+By+Cz+D=0 Y creo recordar que la normal a ese plano era (A,B,C). Una cara no es ms que una porcin de plano. La orden glNormal3f(x,y,z) se pone antes de dibujar una determinada cara, gracias a esa normal, se podr aplicar una iluminacin. Sin embargo si os bajis el tutorial tut_1_4_2.zip y lo compilis y ejecutis sin ms, veris algo as: si lo ejecutis y pulsis la tecla 'S' lo veris as:

La diferencia es notable...pero cul es?. A la hora de modelar, en OpenGL tenemos dos opciones: glShadeModel(GL_SMOOTH) glShadeModel(GL_FLAT); Para poder utilizar GL_SMOOTH (2 imagen), debemos especificar normales POR VRTICE y no por cara. Si especificamos una normal por cara, entonces a la hora de la iluminacin, esa cara se sombrear entera de la misma forma (ver imagen 1, se nota cada una de las caras). En el caso de que especifiquemos una normal por vrtice, podremos hacer una sombreado suave. Recordad: Normal por cara: GL_FLAT. Normal por vrtice: GL_SMOOTH (aunque podemos utilizar GL_FLAT).

Sin en las normales por cara, sta expresa la normal al plano, en la normal por vrtice debera expresarse la media de las nomales de las caras que une ese vrtice. Si lo pensis es bastante intuitivo.

Apartado 3: CARGA DE MODELOS DE FICHEROS EXTERNOS Hasta ahora, hemos dibujado cubos, tringulos...siempre a mano, especificando cada uno de sus vrtices. En el cdigo tut_1_4_2.zip, haremos esto de forma diferente. Hace un par de semanas, puse en la pgina un documento y unas utilidades llamadas madutils. (Vase Fase 2- Tutorial 1). En ese documento se especifica el formato de modelos MAD. Existen otros formatos (y posiblemente mejores) como el del 3D Studio (3DS) y los de los Quakes. En el cdigo 1.2.4 vamos a ver algunos ficheros nuevos (mad.c y mad.h) que son los que tratan de este tema. Lo que vamos a intentar conseguir no es ms que tener en memoria una estructura de datos, que van a ser un conjunto de caras, que vamos a recorrer a la hora de dibujar el modelo. Veamos estas estructuras: /* Estructura vertice*/ typedef struct vector_tag { float x, y, z; // Coordenadas espaciales float u, v; // Coordenadas de Mapeo de textura float Nx,Ny,Nz; // Coordenadas de Normal del vrtice } vertice; /* Estrucutura cara (un tringulo) */ typedef struct tag_face { vertice vertices[3]; // Puntos del tringulo } cara;

/* Estrucutura modelo */ typedef struct tag_object { unsigned int n_caras; // Numero de caras cara *triangulos; // Matriz de caras char id_textura[16]; // Fichero de textura unsigned int n_textura; // Identificacin de textura float x, y, z; // Posicin en el espacio vertice base[3]; // Base; } modelo; A grandes rasgos, el objetivo de la funcin carga_mad() (en mad.c) es coger los datos del fichero y meterlos en memoria. La especificacin del formato MAD est en el documento anteriormente citado. En memoria vamos a tener una cabecera (estructura modelo), con un puntero (triangulos) que apuntar a un array de caras. (Mirad la estructura caras, no es ms que un conjunto de tres vrtices). Tenemos otros registros que no utilizaremos por ahora (x,y,z y base). En cuestin, la cabecera modelo nos va a informar de los datos que necesitamos saber de ese modelo en cuestin.) No pongo aqu la

funcin carga_mad por ser bastante larga, pero es bastante simple, slo hay que controlar un poco el tema de punteros y E/S. El cdigo en gl_renderiza_escena , quedara as: glBindTexture(GL_TEXTURE_2D, textura); /* No ms modelado a mano. Recorreremos la estructura del modelo, e iremos dicujando paulatinamente */ for (j = 0; j < objeto->n_caras; j++) { /* Entramos en las caras y rellenamos variables auxiliares para mayor legibilidad*/ view_x1 = objeto->triangulos[j].vertices[0].x; view_y1 = objeto->triangulos[j].vertices[0].y; view_z1 = objeto->triangulos[j].vertices[0].z; view_x2 = objeto->triangulos[j].vertices[1].x; view_y2 = objeto->triangulos[j].vertices[1].y; view_z2 = objeto->triangulos[j].vertices[1].z; view_x3 = objeto->triangulos[j].vertices[2].x; view_y3 = objeto->triangulos[j].vertices[2].y; view_z3 = objeto->triangulos[j].vertices[2].z; /* Ahora dibujamos la cara usando mapeado de texturas y normales en cada vrtice*/ glBegin(GL_TRIANGLES); glTexCoord2f (objeto->triangulos[j].vertices[0].u,objeto->triangulos[j].vertices[0].v); glNormal3f(objeto->triangulos[j].vertices[0].Nx,objeto->triangulos[j].vertices[0].Ny,objeto>triangulos[j].vertices[0].Nz); glVertex3f (view_x1, view_y1, view_z1); glTexCoord2f (objeto->triangulos[j].vertices[1].u,objeto->triangulos[j].vertices[1].v); glNormal3f(objeto->triangulos[j].vertices[1].Nx,objeto->triangulos[j].vertices[1].Ny,objeto>triangulos[j].vertices[1].Nz); glVertex3f (view_x2, view_y2, view_z2); glTexCoord2f (objeto->triangulos[j].vertices[2].u,objeto->triangulos[j].vertices[2].v); glNormal3f(objeto->triangulos[j].vertices[2].Nx,objeto->triangulos[j].vertices[2].Ny,objeto>triangulos[j].vertices[2].Nz); glVertex3f (view_x3, view_y3, view_z3); glEnd(); } Como vis, simplemente vamos recorriendo el conjunto de caras en memoria.

Este cdigo implementa tb un par de cosas nuevas. Hay un apuntador a fichero global (en init_sys.h) donde se redirigirn los mensajes no crticos (que no paren el programa) y alguna que otra informacin. El fichero creado ser w3d_logs.txt. Adems tenemos un par de ficheros nuevos (teclado.h y teclado.c) que contendrn acciones que haya que realizar ante la pulsacin de una tecla. sys_win32. y sys_linux.c son los que llamarn a este fichero. Por ltimo: Con esto, nos quitamos el problema de realizar los modelados a mano...pero hay que hacerlos de alguna u otra manera. Para ello estn las madutils . Son dos aplicaciones (un visor y un conversor ) que os podis bajar de aqu. El conversor pasa modelos del Quake2 a formato MAD. Entonces, para hacer un modelo slo os hace falta un editor de modelos del Quake2. Pasaos por esta otra seccin y bajaos el que os recomiendo, el Quake 2 Modeller 0.90b. NOTA: Es una buena filosofa adaptar lo que hagamos de tal manera que podamos utilizar herramientas existentes para la creacin de nuestro engine 3d. Este editor es bastante bueno, y permite importar modelos de formato 3DS y ASC. Existen otros modeladores que se supone tambin servirn. Las madutils estn en versin beta, y depende de todos que sigan evolucionando. Bueno, eso esto es todo por ahora. Reconozco que este tutorial puede llegar a ser un poco complicado, pero no dudis en escribirme o acudir al foro. La mejor manera de aprender es coger el cdigo y despanzurrarlo. Espero que os haya gustado.

WORLDSPACE 3D - TUTORIAL 1.5 - DOCUMENTACIN


Este es el nuevo formato para los tutoriales. Puede que sea un poco principio al coazo, pero creedme, una vez os acostumbris, veris que este sistema de documentacin muy bueno, y perfecto para seguir todo el cdigo del tutorial. Adems, es mucho ms cmo para mi, ya que puedo generar el tutorial HTML directamente usando los comentarios de las fuentes. As gasto menos tiempo haciendo el tutorial escribiendo cosas que ya existen en las fuentes. En este tutorial nos encontraremos nuevas cosas: 1. Configuracin de la aplicacin desde fuera ( w3_cfg.txt). No har falta recompilar para poner a FULLSCREEN Salida logs ms detallada en el fichero w3d_logs.txt Fuentes mapeadas. Escribiremos en pantalla utilizando dos nuevas funciones (ver ficheros fuentes.*). Este sistema es uno de los que hay para escribir caracteres en OpenGL. Lo he elegido porque es fcil, no dependiente del sistema (Linux=Win32), rpido, y se pueden cambiar las fuentes desde fuera. Mirad los archivos de fuentes def.tga y agulon.tga. Las fuentes las cree utilizando un programa que se llama font builder y podis obtener en www.lmnopc.com. Luego las retoqu con el Paint Shop Pro. Hay que ponerles un canal ALPHA. Lo mejor que podis hacer es ver las fuentes que vienen y fijaros en como est hechas. Timing'; o sea, medida del tiempo. Con esto podremos calcular los FPS Veremos como dibujar bitmaps de fondo utilizando texturas. Prestad atencin a la funcin gl_renderiza_escena, ha cambiado bastante

2. 3.

4. 5.

6.

Sistema de directorio. El directorio base es ws_datos. Esto se hace para que, si por ejemplo, qureis cambiar los ficheros TGA, hacis un directorio hermano a w3_datos y ponis all vuestros ficheros (siguiendo la estructura original) cambiando base_dir del fichero de configuracin.

Y eso es todo... a aprender y a disfrutar...

Tutorial 1.6 - El espacio 3D neuralgya w3d@lycos.es El espacio 3D (de nuevo) Integrando clculos y OpenGL Timers y teclado Dibujando modelos 3D y 2D Introduccin En este tutorial resumiremos lo aprendido hasta ahora. Carga de texturas, carga de modelos, posicionado, modelado y movimiento en un espacio 3D. Se usarn algunas funciones de escritura de fuentes tambin, y funciones de timing. Es importante comentar que este tutorial se ha realizado bsicamente sobre Linux y Windows, usando gcc y Dev-C++. He dejado de lado el Visual C++ y el lccwin32. Las razones: Visual C++ es una herramienta muy buena, pero es propietaria y no todo el mundo puede obtenerla. Escog Dev-C++ en vez de lccwin32 por cuestiones de gustos simplemente. De todas maneras, el cdigo es portable, y de hecho, algn voluntario puede crear el fichero de proyecto para Visual C++ y mandrmelo, que se publicar en la web. Otro cambio fundamental es la utilizacin de la librera SDL. SDL (Simple Directmedia Layer) es una librera que interopera estupendamente con OpenGL y es multiplataforma. Sus funciones de E/S, timing y video nos permitirn que el cdigo sea portable (apenas una lnea de diferencia entre Win32 y Linux). En este tutorial se usa para manejar las teclas, el ratn, los timers y la inicializacin grfica. Es sencillo (comparar el cdigo para abrir una ventana OpenGL en los antiguos tutoriales con este), es rpido, es software libre y es portable. Esto nos permite centrarnos en OpenGL. En breve publicar un tutorial para que veis lo fcil que es instalarlo. Y sin ms dilaciones, empezamos con el 'famoso' tutorial 1.6. El espacio 3D (de nuevo)

Uno de los objetivos primarios de este nuevo tutorial es el de orientarse en el espacio 3D usando matrices. Si recordamos el tutorial 1 (Matrices y aspectos matemticos), nos vendr a la cabeza esta figura:

Como podremos observar, tenemos dos sistemas realmente importantes. objeto y camara. Objeto es el sistema segn el cual nosotros vamos a definir los puntos que forman un modelo. Esto es, si queremos dibujar una nave (modelo), todos los puntos de sta se dibunan respecto al sistema de coordenadas objeto. Si observamos las especificaciones del formato MAD, por ejemplo, vemos que no es ms que un conjunto de puntos con una base, que no es ms que la matriz objeto que define la posicin y orientacin de se sistema de coordenadas. Pero a la hora de visualizar este objeto, debemos pasar todos sus puntos al sistema de coordenadas cmara. El sistema de coordenadas cmara es aquel que define la posicin y orientacin del observador, o sea, nosotros. Si recordamos todava ms el tutorial 1, veamos que esto se resuma en lo siguiente: P(respecto a 'camara')=B' x (A x P) Siendo P un punto genrico del objeto a dibujar (p.ej. la nave), B' la matriz inversa de B (que define el sistema de coordendas de la cmara), A la matriz que define el sistema de coordenadas del objeto, y P un punto genrico del modelo. Bien. Pues OpenGL hace todo esto por nosotros de la manera siguiente: Llammos al sistema de coordenadas del objeto matriz MODELVIEW, y al sistema de coordenadas de la cmara matriz PROJECTION, seguro que esto os suena de verlo rulando por el cdigo fuente. Si nos fijamos en el cdigo de casi cualquier tutorial podremos ver esto: glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0f, (GLfloat) config.SCREEN_SIZE_X / (GLfloat) config.SCREEN_SIZE_Y, 0.01f, 100000.0f); Ese cdigo lo podemos encontrar en la funcin init_gl() en el fichero render.c. Lo que hacemos con esto es preparar la matriz de proyeccin (PROJECTION). Como deca, esta matriz se encarga de pasar coordenadas del sistema origen o global al sistema cmara. Pero, hay que darse cuenta que nuestra cmara es en realidad la pantalla del ordenador, un sistema bidimiensional y con unas cuantas limitaciones. Con estas funciones lo que hacemos es preparar esa matriz de conversin para adaptar los

puntos a una pantalla que no es cuadrada, decirle que como nuestra cmara es un sistema bidimensional ajuste los puntos mediante perspectiva, ngulo de visin y planos de corte (para no dibujar los puntos que estn o muy cerca o muy lejos). Pero todava no queda ah la cosa. Hasta ahora hemos adaptado la matriz de proyeccin a la pantalla fsica del ordenador, esto es un paso necesario y que no describ en el tutorial 1, que es ms genrico. Si seguimos buceando en el cdigo fuente vemos lo siguiente en el ficherorender.c funcin gl_renderiza_escena: glMatrixMode(GL_PROJECTION); gluLookAt( camara[3][0],camara[3][1],camara[3][2], // Posicin // Punto

camara[3][0]+camara[2][0],camara[3][1]+camara[2][1],camara[3][2]+camara[2][2], al que miramos camara[1][0],camara[1][1],camara[1][2]); // Orientacin Y

Vamos a obviar las variables. sta funcin de la librera GLU prepara la matriz de proyeccin para que pase puntos del espacio origen o global a un sistema de coordenadas definido por la matriz cmara. Vamos a tener en la matriz PROJECTION una rplica de lo que sera la matriz cmara, el sistema de coordendas cmara. En resumen. La matriz de proyeccin (PROJECTION) es la matriz que convierte coordenadas respecto al sistemas de coordendas origen al sistema de coordenadas de la cmara. La definimos con gluLookAt(). Pero OJO, la matriz camara[][] , que es la que nosotros manejamos en nuestro cdigo, NO ES IGUAL a la matriz interna PROJECTION de OpenGL, ya que previamente habamos definido con gluPerspective() que adems pasase las coordenadas a coordenadas de la pantalla. OpenGL realiza un paso ms que el definido en nuestra teora. Nosotros nos quedbamos con los puntos respecto al sistema cmara (un sistema TRIDIMENSIONAL genrico), pero OpenGL, con la matriz PROJECTION "tocada" por gluPerspective() los pasa a un sistema BIDIMENSIONAL: nuestra pantalla. El caso de la matriz MODELVIEW es todava ms simple. Esta matriz sera la matriz objeto, encargada de convertir coordenadas del modelo-objeto a coordendas globales. Vemos este cdigo (fichero render.c en la funcin gl_renderiza_escena() : glMatrixMode(GL_MODELVIEW); [...] auxiliar_objeto[0][0]=nave1->base[0].x; auxiliar_objeto[0][1]=nave1->base[0].y; auxiliar_objeto[0][2]=nave1->base[0].z; auxiliar_objeto[0][3]=0;

auxiliar_objeto[1][0]=nave1->base[1].x;

auxiliar_objeto[1][1]=nave1->base[1].y; auxiliar_objeto[1][2]=nave1->base[1].z; auxiliar_objeto[1][3]=0;

auxiliar_objeto[2][0]=nave1->base[2].x; auxiliar_objeto[2][1]=nave1->base[2].y; auxiliar_objeto[2][2]=nave1->base[2].z; auxiliar_objeto[2][3]=0;

auxiliar_objeto[3][0]=nave1->x; auxiliar_objeto[3][1]=nave1->y; auxiliar_objeto[3][2]=nave1->z; auxiliar_objeto[3][3]=1;

glLoadMatrixf(&auxiliar_objeto); Bien, esto lo que hace es simplemente cargar en la matriz interna de OpenGL (MODELVIEW) la matriz asociada al modelo que queremos dibujar. A partir de ste momento, todas las coordendas van a estar expresadas respecto al origen del sistema objeto. Sencillo no?. Integrando clculos y OpenGL Si lo pensamos bien, vemos que la matriz de proyeccin (PROJECTION), se crea en dos fases, con gluPerspective() y con gluLookAt(). Podemos darnos cuenta que la primera parte es fija. Se calcula al inicializar OpenGL. La modificacin que establece gluLookAt() es dinmica ya que depende de la posicin de la cmara. Echmos un vistazo a este cdigo en init_gl(): /* Vamos a preparar las matrices de proyeccin */

glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0f, (GLfloat) config.SCREEN_SIZE_X / (GLfloat) config.SCREEN_SIZE_Y, 0.01f, 100000.0f);

glGetDoublev(GL_PROJECTION_MATRIX,&matriz_proyeccion_A[0][0]);

glLoadIdentity(); gluPerspective(45.0f, (GLfloat) config.SCREEN_SIZE_X / (GLfloat) config.SCREEN_SIZE_Y, 0.01f, 100000.0f);

glGetDoublev(GL_PROJECTION_MATRIX,&matriz_proyeccion_B[0][0]);

glLoadIdentity(); glOrtho(0,config.SCREEN_SIZE_X,0,config.SCREEN_SIZE_Y,-100,100); glGetDoublev(GL_PROJECTION_MATRIX,&matriz_proyeccion_C[0][0]);

glLoadMatrixd(&matriz_proyeccion_A[0][0]);

fprintf(logs,"Matrices de proyeccin creadas\n"); Qu co#@# es esto?. Fcil. Vamos a guardar en matriz_proyeccion_(A|B|C) varias matrices de proyecin. Por qu?, pues porque en el programa usaremos varias. matriz_proyeccion_A es la normal y la llevamos usando desde siempre, matriz_proyeccion_C es usada para las fuentes y renderizado directo en la pantalla. Asi pues, tendremos estas matrices de proyeccin guardadas, por lo que a la hora de renderizar, solo habr que cargarlas y aplicarles un gluLookAt(). Vemos un resumen de la funcin gl_enderiza_escena(), donde pone [...] significa que existe ms cdigo, pero ahora no es relevante:

glMatrixMode(GL_PROJECTION); glLoadMatrixd(&matriz_proyeccion_B[0][0]);

gluLookAt( camara[3][0],camara[3][1],camara[3][2],

// Posicin

camara[3][0]+camara[2][0],camara[3][1]+camara[2][1],camara[3][2]+camara[2][2], // Punto al que miramos

camara[1][0],camara[1][1],camara[1][2]);

// Orientacin Y

glMatrixMode(GL_MODELVIEW);

[...] /* Comenzamos a dibujar la nave de marras */ [...] glLoadMatrixf(&auxiliar_objeto); for (j = 0; j < nave1->n_caras; j++) { glBegin(GL_TRIANGLES); [...] glEnd(); } glLoadIdentity(); /* Dibujado de las fuentes */ [...] glMatrixMode(GL_PROJECTION); glLoadMatrixd(&matriz_proyeccion_C[0][0]); [...] /* CrossHair */ [...] /* Localizador */ [...] /* Calculamos e imprimimos los frames por segundo */ print(fuentes[1],0.0f,config.SCREEN_SIZE_Y,.5f,"Velocidad %f",velocidad); /* Incrementamos el nmero de frames visualizados */ FPS++; /* Llamamos a la funcin de intercambio de buffers*/ sys_swapbuffers(); Como vemos, sacamos la ventaja de que no necesitamos calcular la matriz de proyeccin completamente en cada renderizado. Si nos vamos ahora al fichero teclado.c, veremos por all lo siguiente: SDL_GetMouseState(&mouse_x,&mouse_y);

glMatrixMode(GL_MODELVIEW); x=camara[3][0]; y=camara[3][1];

z=camara[3][2]; camara[3][0]=0; camara[3][1]=0; camara[3][2]=0; camara[3][3]=1; glLoadMatrixf(&camara[0][0]); glRotatef((config.SCREEN_SIZE_X/2-(mouse_x))*intervalo/6.0f,0.0f,1.0f,0.0f); glRotatef(((mouse_y)-config.SCREEN_SIZE_Y/2)*intervalo/6.0f,1.0f,0.0f,0.0f); glGetFloatv(GL_MODELVIEW_MATRIX,&camara[0][0]); camara[3][0]=x; camara[3][1]=y; camara[3][2]=z; camara[3][3]=1; Lo que hacemos aqu es lo siguiente: 1. 2. 3. 4. Primero cogemos la matriz camara y guardamos su punto de posicin en x,y,z. Ponemos el punto de posicin a 0,0,0. Cargamos la matriz en la pila de OpenGL. Rotamos segn eje Y y eje X usando como ngulo un clculo obtenido de la posicin del ratn y el intervalo de tiempo transcurrido. OpenGL guarda la matriz resultante en la pila, y nosotros la guardamos con glGetFloatv(GL_MODELVIEW_MATRIX,&camara[0][0]); en la matriz camara. Restauramos en camara el punto de posicion.

5.

6.

NOTA: Cambiamos el punto de posicion de camara a 0,0,0 para que la rotacion se haga sobre si misma, si no se hara sobre el eje de coordenadas global. Con esto nos ahorramos tener que hacer inversas multiplicaciones de matrices para hacer rotaciones. Asi pus jugando con las matrices MODELVIEW y PROJECTION de OpenGL, nos ahorramos bastante cdigo matemtico. Timers y teclado Bueno, cambiamos de "tercio". Vamos a usar un timer global para llevar un control sobre el tiempo. En todos los juegos se debe hacer esto, entre otras cosas, para que la velocidad sea siempre la misma sea el ordenador en el que se ejecute. Nosotros vamos a usar las funciones de timer de SDL, que son multiplataforma y bien simples:

/* Tiempos de inicio y el total */ unsigned int past=0; /*! Inicializa el timer */ int ini_timer(void) { past = SDL_GetTicks(); past = SDL_GetTicks(); fprintf(logs,"Timer inicializado en %u\n",past); return 0; } /*! devuelve el numero de milisegundos transcurridos desde la inicializacion del timer (Qu facil con SDL)*/ unsigned int get_timer(void) { return ((unsigned int)SDL_GetTicks()-past); } Este cdigo esta en el fichero timer_linux.c. A pesar del nombre el cdigo es el mismo para Win32 y Linux. Es un remanente de viejos tutoriales ;P. El cdigo es bastante simple, la funcin get_timer() devolver el nmero de milisegundos pasados desde el inicio de la ejecucin del timer en ini_timer() while (done==0) { taux = get_timer(); intervalo = (taux - tiempo_antiguo)/1000.0f; tiempo_antiguo = taux; if (SDL_PollEvent(&event)) if (event.type == SDL_QUIT) done = 1; if (tecla_pulsada() == -1) done = 1; input_mouse(); gl_renderiza_escena(); }

ste es el bucle principal del programa en sys_linux.c. (El nombre es otro remanente, ese fichero es comn en Win32 y Linux. Como vemos se calcula el intervalo (en segundos) de tiempo pasado en cada iteraccin del bucle. Este mismo intervalo se usa despus en teclado.c para calcular los grados de giro, el avance, etc, etc.: if (keys[SDLK_d]) { glMatrixMode(GL_MODELVIEW); x=camara[3][0]; y=camara[3][1]; z=camara[3][2]; camara[3][0]=0; camara[3][1]=0; camara[3][2]=0; camara[3][3]=1; glLoadMatrixf(&camara[0][0]); glRotatef(60.f*intervalo,0.0f,0.0f,1.0f); glGetFloatv(GL_MODELVIEW_MATRIX,&camara[0][0]); camara[3][0]=x; camara[3][1]=y; camara[3][2]=z; camara[3][3]=1; } [...] /* Actualizacin de variables dinmicas */ velocidad=10.0f-(10/velocidad_r); camara[3][2] += (velocidad * intervalo* camara[2][2]); camara[3][1] += (velocidad * intervalo* camara[2][1]); camara[3][0] += (velocidad * intervalo* camara[2][0]); [...] NOTA: Velocidad es un clculo algo "especial". La variable velocidad_r lleva un valor entre 1.0 e infinito dependiendo del tiempo que est pulsada la tecla 'w' o 'a'. Sin embargo, la velocidad real es la variable velocidad que se calcula a partir de sta y va de un rango de 0 a 10. En cuanto al teclado, bueno, usamos SDL tambin, y creo que las funcione son bastante simples como para que os diga nada. Echadles un vistacillo y ya est

Por cierto, la nica diferencia entre Windows Y Linux, habalndo de cdigo por supuesto, est en el fichero sys_linux.c: #ifdef _LINUX int main(int argc, char *argv[]) { #endif #ifdef _WIN32W3D int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE LPSTR int { #endif Dibujando modelos 3D y 2D A estas alturas, es fcil explicar el modelado. Vemos el cdigo en gl_renderiza_escena(): glEnable(GL_TEXTURE_2D); auxiliar_objeto[0][0]=nave1->base[0].x; auxiliar_objeto[0][1]=nave1->base[0].y; auxiliar_objeto[0][2]=nave1->base[0].z; auxiliar_objeto[0][3]=0; auxiliar_objeto[1][0]=nave1->base[1].x; auxiliar_objeto[1][1]=nave1->base[1].y; auxiliar_objeto[1][2]=nave1->base[1].z; auxiliar_objeto[1][3]=0; hPrevInstance, lpCmdLine, nCmdShow)

auxiliar_objeto[2][0]=nave1->base[2].x; auxiliar_objeto[2][1]=nave1->base[2].y; auxiliar_objeto[2][2]=nave1->base[2].z; auxiliar_objeto[2][3]=0;

auxiliar_objeto[3][0]=nave1->x; auxiliar_objeto[3][1]=nave1->y; auxiliar_objeto[3][2]=nave1->z; auxiliar_objeto[3][3]=1;

glLoadMatrixf(&auxiliar_objeto);

glBindTexture(GL_TEXTURE_2D, nave1->n_textura); for (j = 0; j < nave1->n_caras; j++) { glBegin(GL_TRIANGLES); glTexCoord2f(nave1->triangulos[j].vertices[0].u, nave1->triangulos[j].vertices[0].v); glNormal3f(nave1->triangulos[j].vertices[0].Nx, nave1->triangulos[j].vertices[0].Ny, nave1->triangulos[j].vertices[0].Nz); glVertex3f(nave1->triangulos[j].vertices[0].x,nave1->triangulos[j].vertices[0].y,nave1>triangulos[j].vertices[0].z); glTexCoord2f(nave1->triangulos[j].vertices[1].u, nave1->triangulos[j].vertices[1].v); glNormal3f(nave1->triangulos[j].vertices[1].Nx, nave1->triangulos[j].vertices[1].Ny, nave1->triangulos[j].vertices[1].Nz); glVertex3f(nave1->triangulos[j].vertices[1].x,nave1->triangulos[j].vertices[1].y,nave1>triangulos[j].vertices[1].z); glTexCoord2f(nave1->triangulos[j].vertices[2].u, nave1->triangulos[j].vertices[2].v); glNormal3f(nave1->triangulos[j].vertices[2].Nx, nave1->triangulos[j].vertices[2].Ny, nave1->triangulos[j].vertices[2].Nz);

glVertex3f(nave1->triangulos[j].vertices[2].x,nave1->triangulos[j].vertices[2].y,nave1>triangulos[j].vertices[2].z); glEnd(); } Esto es una rplica casi exacta del cdigo del tutorial 1.4. El nico truco es el comentado anteriormente de cargar la base o matriz del sistema de coordenadas del modelo en la matriz MODELVIEW. Si os fijis, hay un par de cosas nuevas en este tutorial. Una de ellas es el crosshair, sa mirilla que est en todo momento en el centro de la pantalla. Cmo se dibuja?. Bueno, en primer lugar debemos cambiar la matriz de proyeccin, ya que ahora vamos a variar un poco el modo de dibujar. As que cargamos la matriz de proyeccin C. (matriz_proyeccion_C[][]) . glLoadMatrixd(&matriz_proyeccion_C[0][0]); glClear(GL_DEPTH_BUFFER_BIT); glBindTexture(GL_TEXTURE_2D, textura[3]); /* CrossHair */ /* Con la funcion glTexCoord2f mapeamos las coordenadas de la textura*/ glBegin(GL_QUADS); glTexCoord2f(0.0f, 1.0f); glVertex2f(config.SCREEN_SIZE_X/216.0f,config.SCREEN_SIZE_Y/2+16.0f); glTexCoord2f(0.0f, 0.0f); glVertex2f(config.SCREEN_SIZE_X/2-16.0f,config.SCREEN_SIZE_Y/216.0f); glTexCoord2f(1.0f, 0.0f); glVertex2f(config.SCREEN_SIZE_X/2+16.0f,config.SCREEN_SIZE_Y/216.0f); glTexCoord2f(1.0f, 1.0f); glVertex2f(config.SCREEN_SIZE_X/2+16.0f,config.SCREEN_SIZE_Y/2+16.0f); glEnd(); Se dibuja un simple cuadrado con la textura del crosshair. Notemos que las coordenadas ahora representan los pxeles en la pantalla. (0,0) es la esquina superior izquierda. Es por ello que se usa una matriz de proyeccin diferente. Ahora nos interesa pintar sobre la pantalla directamente y no sobre un espacio 3D. Es importante que os deis cuenta de que el crosshair se dibuja al final y con el buffer de profundidad BORRADO, porque siempre se ve en primer plano. Si os fijis, las fuentes usan esta misma matriz de proyeccin Otro truquillo son las lneas que marcan la posicin de la nave "liebre" para facilitar su localizacin cuando est muy lejos. glRasterPos3f(nave1->x,nave1->y,nave1->z); glGetBooleanv( GL_CURRENT_RASTER_POSITION_VALID,&visible);

glGetFloatv( GL_CURRENT_RASTER_POSITION,&viewport); glLoadIdentity(); [...] glLoadMatrixd(&matriz_proyeccion_C[0][0]); [...] /* Localizador */ if (visible) { glBindTexture(GL_TEXTURE_2D, textura[4]); glBegin(GL_QUADS); glTexCoord2f(0.0f, 1.0f); glVertex2f(viewport[0]-16.0f,viewport[1]+16.0f); glTexCoord2f(0.0f, 0.0f); glVertex2f(viewport[0]-16.0f,viewport[1]-16.0f); glTexCoord2f(1.0f, 0.0f); glVertex2f(viewport[0]+16.0f,viewport[1]-16.0f); glTexCoord2f(1.0f, 1.0f); glVertex2f(viewport[0]+16.0f,viewport[1]+16.0f); glEnd(); } Con las primeras 3 lneas lo que hacemos es consultar con OpenGL dnde se situara un punto en la pantalla. Este punto es la posicin de la nave "liebre". Digamos que OpenGl hace todos los clculos necesarios para dibujar se punto 3D en la pantalla, pero no lo dibuja. Guardamos en viewport la posicin RASTER, esto es, las coordenadas de la pantalla donde se dibujara se punto, y en visible, si sas coordenadas estn dentro de la pantalla Ms tarde, ya con la matriz de proyeccin C cargada, si visible es verdadero, dibujamos una caja con textura y centro el punto que habamos calculado con glRasterPos. Fcil eh? Ms cosillas...las estrellas. Lo que haremos es crear una lista de puntos, que se calculan aleatoriamente, que estn sobre una superficie esfrica de radio 6000 y su grosor vara entre 1 y 3. Veamos la funcin crea_estrellas():

int crea_estrellas(unsigned int numero) { float z,y,x; unsigned int i; /* Punto aleatorio de una esfera de radio 6000 */ estrellas=(float *)malloc(sizeof(float)*4*numero); for (i=0;i < numero*4;i+=4) { z=(float)(rand() % 6000);

y=rand()%((int)sqrt(pow(6000,2)-pow(z,2))); x=sqrt(pow(6000,2)-pow(y,2)-pow(z,2)); if (rand()%2==0) z=z*-1; if (rand()%2==0) x=x*-1; if (rand()%2==0) y=y*-1; estrellas[i]=x; estrellas[i+1]=y; estrellas[i+2]=z; estrellas[i+3]=(float)(rand()%2+1); } n_estrellas=numero; } Para dibujarlas, lo hacemos al principio de renderizar la escena: for (aux=0;aux < n_estrellas;aux+=4) { glPointSize(estrellas[aux+3]); glBegin(GL_POINTS); glVertex3f(estrellas[aux],estrellas[aux+1],estrellas[aux+2]); glEnd(); } Los planetas de fondo son en realidad sprites. Lo que hice fue hacer un pequeo programa que generaba un fichero aleatorio de este tipo: (ws_datos/espacios/espacio1.txt) < TIPO>SPRITE < TEXTURA>texturas/nebulosa.tga < POSICION>1699.925049,2033.117310,5383.000000 < EJE_X>0.843889,0.356345,-0.401085 < EJE_Y>-0.455609,0.870745,-0.184994 < EJE_Z>0.283321,0.338853,0.897167

< TAMA_X>492.000000 < TAMA_Y>500.000000 < TIPO>SPRITE < TEXTURA>texturas/quasar2.tga < POSICION>-4422.473633,2176.805908,3421.000000 < EJE_X>0.669650,0.278468,0.688494 < EJE_Y>0.091013,0.889287,-0.448203 < EJE_Z>-0.737079,0.362801,0.570167 < TAMA_X>165.000000 < TAMA_Y>500.000000 < TIPO>SPRITE [...] La tcnica es similar a la de las estrellas. Una serie de cajas bidimensionales, o sea, caras cuadrilteras que miran al centro de una esfera de radio 6000 y superficie aleatoria a las que se le aplican una textura. La funcin carga_espacio en espacio.c se encarga de leer este fichero y crear una lista del tipo lista_sprites_spc_tag (ver estructuras.h), la cual se recorrer posteriormente justo despus de dibujar las estrellas: glEnable(GL_TEXTURE_2D); for (aux=0;aux < n_sprites_spc;aux++) { glLoadMatrixf(&sprites_spc[aux].matriz); glBindTexture(GL_TEXTURE_2D, sprites_spc[aux].textura); aux2=sprites_spc[aux].tam_x; glBegin(GL_QUADS); glTexCoord2f(1.0f, 1.0f); glVertex2f( aux2, aux2); glTexCoord2f(1.0f, 0.0f); glVertex2f( aux2, -aux2); glTexCoord2f(0.0f, 0.0f); glVertex2f(-aux2, -aux2); glTexCoord2f(0.0f, 1.0f); glVertex2f(-aux2, aux2); glEnd(); }

Você também pode gostar