You are on page 1of 73

Programacin OpenGL: Escenas 3D

por Miguel Angel Seplveda


Despus de un largo camino por otros temas, finalmente ha llegado el momento de hablar de grficos en 3D bajo OpenGL. No voy a mentir diciendo que es un tema fcil porque no lo es. Cualquier buen programador de aplicaciones 3D con OpenGL, y en particular de animaciones, debe tener suficientes conocimientos de lgebra lineal, geometra analtica, fsica (mecnica) y por supuesto dominar el anlisis numrico. Intentar hacer el resto de esta serie lo mas accesible posible para todo el mundo. Desafortunadamente, no hay forma de evitar la necesidad de tener conocimientos sobre matrices, cmo los planos y lneas se representan matemticamente en el espacio 3D, vectores, aproximaciones polinomiales a curvas, por mencionar solo unos pocos. Durante la ltima semana he estado pensando como presentar este material ms complejo a una audiencia amplia. Los libros clsicos utilizan una metodologa incremental paso a paso, ms o menos el mtodo que he seguido en los dos artculos anteriores. He decidido no seguir con esta metodologa pues se necesitara demasiado tiempo (meses) para conseguir llevar al lector al punto de poder escribir su propio cdigo. En su lugar voy a aventurarme a utilizar otro mtodo, que he decidido llamarlo "tratamiento de choque". Esta vez voy a incluir una demostracin de una de mis simulaciones 3D y luego explicare bit por bit qu hace el cdigo. Finalmente, intentar explicar con ms detalle todas las cuestiones que normalmente se tratan en los libros estndar de OpenGL. Creo que saltando directamente al final y dando al lector un ejemplo de cdigo con algunas cosas interesantes incitar a los lectores a experimentar y a probar cosas, incluso aunque yo no haya dicho aun como funcionan todas las cosas exactamente. Espero que este mtodo funcione y la gente lo encuentre rpido y ms directo. As que pongamos manos a la obra. Durante los 6 meses anteriores he estado trabajado en la universidad de Pittsburgh en una herramienta OO (Object Oriented) para el desarrollo de simulaciones de polmeros y geles. El proyecto est bastante avanzado, la fsica es muy interesante, incluso para informticos, porque un gel es bsicamente una red neuronal de polmeros y muchas de las tcnicas desarrolladas para redes neuronales se

Programacin OpenGL: Escenas 3D


por Miguel Angel Seplveda

pueden aplicar a la construcciones del gel. He elegido unos cuantos objetos de esta herramienta y los he empaquetado en esta sencilla demo: ../../common/May1998/example2.tar.gz. Se puede compilar bajo Linux, cualquier UNIX o sobre Windows 95/NT (suponiendo que ya tienes instalado GLUT). La demo muestra un polmero simple (una cadena lineal de monmeros enlazados) movindose en suspensin en una solucin a una determinada temperatura. La dinmica es tentadora, se asemeja a una serpiente excitada. La animacin es muy viva debido a las colisiones de las molculas del solvente. No se puede ver el solvente, pero influye en el movimiento del polmero a travs de las ecuaciones del movimiento. El modelo utilizado para dibujar el polmero es bastante sencillo; example2 controla las coordenadas (x, y, z) de cada nodo (monmero) a lo largo de la cadena polmero. En cada imagen (frame) de la animacin dibujamos una esfera en las coordenadas del monmero y luego los unimos utilizando cilindros que van de monmero a monmero. Por tanto tenemos dos primitivas elementales 3D: una esfera y un cilindro. Como en cualquier molcula, la distancia entre monmeros cambia en el tiempo, con lo que no podemos utilizar "un cilindro" para dibujar todos los enlaces, se ha de re-escalar de acuerdo a la distancia entre cada par de monmeros. Primera pregunta: Has dicho que tienes dos objetos en 3D, una esfera y un cilindro unidad. digamos que ambos objetos estn centrados en el origen de coordenadas. Si todo lo que sabemos sobre el polmero es la secuencia de (x, y, z) de los nodos, cmo podemos escalar, rotar y trasladar las replicas de los cilindros para crear los enlaces de los polmeros?

Por alguna razn que no logro comprender, los cientficos informticos han decidido cambiar el significado clsico de las coordenadas cartesianas: x es horizontal, y es vertical y z va en direccin al observador. Ten cuidado con esto porque si vienes de una formacin matemtica te puede confundir bastante. En la parte superior de la ventana de la animacin se muestra informacin sobre el estado de la animacin que te permitir saber en todo momento el tiempo, la temperatura del polmero, temperatura media del polmero, temperatura de la solucin, la friccin del solvente y el ngulo de rotacin de la cmara exterior. Para tener una visin ms amplia del polmero desde todos los lados, la cmara (tu punto de vista) gira lentamente al rededor del centro de gravedad del polmero. De echo la longitud del polmero que he elegido para la demo es tan corta que la rotacin de la cmara no es realmente necesaria, con un poco de tiempo el polmero llega a girar l solo. Sigue adelante y edita el fichero example2.cxx y modifica la definicin de POLYMERLENGTH a un valor entre 2 y 100. La cmara gira porque quiero que el lector se d cuenta de un aparente problema: cambia del sistema de coordenadas. El sistema de coordenadas de los nodos es utilizadas por las ecuaciones del movimiento y por tato estn expresadas en coordenadas del mundo, independientes del punto de vista concreto desde el que el usuario observa la escena. Estas coordenadas deben proyectarse a las coordenadas 2D x-y de la pantalla del ordenador. Cada vez que cambias el punto de vista, cambian las formulas que transforman las coordenadas internas del polmero en coordenadas 2D de la ventana. Segunda pregunta. Cmo solucionas este problema? cambiando las ecuaciones del movimiento del mundo real a coordenadas 2D del punto de vista no es una solucin, pues requiere demasiada lgebra, es muy complicado de implementar y es difcil no cometer errores. La respuesta a la segunda pregunta es sencilla. Slo hay una opcin: realizar toda la dinmica y la representacin del modelo 3D (polmero) en las coordenadas del mundo y luego cambiar las coordenadas del mundo a las coordenadas 2D del punto de vista de la cmara en el momento de dibujar (render) la imagen. OpenGL es bastante eficiente realizando estas transformaciones, incluso se pueden realizar por hardware (para aquellos que posean una tarjeta grfica con soporte OpenGL vern la diferencia). Pero antes de entrar a describir cmo OpenGL resuelve este problema, consideremos primero cuntas transformaciones de coordenadas hay desde el mundo real en 3D a las coordenadas finales 2D de la ventana.

Primero viene la transformacin de coordenadas del modelo (Modelview), para proyectar las coordenadas originales del mundo a las coordenadas de la vista (eye coordinates), estas son las coordenadas 3D relativas a la posicin del ojo del que mira la escena (o sea las coordenadas de la cmara). Se llama transformacin Modelview porque ----------------- It is called Modelview transformation because it really involves many similar though distinct operations. ----------------- Modelando y viendo proyecciones, lo ltimo es anlogo a posicionar una cmara de fotos en un estudio enfocando hacia la escena que se ha de fotografiar; el modelado de la proyeccin es entonces como posicionar el objeto a fotografiar en frente de la cmara.

Siguiendo la secuencia de transformaciones, las coordenadas de la vista se pasan a las coordenadas de transformacin de la proyeccin. El propsito de estas transformaciones puede parecer un poco esotrico a estas alturas. Despus de posicionar la cmara en la direccin correcta y de posicionar los objetos en el campo de la escena, OpenGL quiere saber qu cantidad (volumen) de campo debe ser proyectado sobre la ventana 2D de la pantalla. Por ejemplo, la cmara puede estar dirigida hacia una montaa muy distante, el campo que vemos define un volumen de espacio muy grande. Los ordenadores slo pueden trabajar con cosas finitas, por ello hay que especificar qu cantidad, de toda la escena, ha de ser recortada. Esta transformacin tambin se encarga de eliminar las superficies que no se pueden ver. Las coordenadas finales obtenidas son las Clip coordinates, recuerda siempre que no es suficientes que tus objetos 3D estn en frente de la cmara, sino que deben estar situados dentro de los planos de recorte definidos por la transformacin de proyeccin. Las distintas perspectivas 3D (como cnica u ortogonal) se definen en este nivel. Por el momento no entraremos en qu es una perspective division, ni cul es la diferencia entre las coordenadas de recorte y las coordenadas normalizadas de dispositivo. No es necesario saberlo aun. La ltima transformacin de coordenadas importante es la transformacin del Viewport. Aqu las coordenadas 3D que han pasado por todo tipo de transformaciones 3D son finalmente proyectadas en el rea 2D de la ventana de tu ordenador. Las transformaciones de coordenadas se representan por matrices (matrices de dos dimensiones). Para cada uno de los anteriores tipos de transformaciones hay una matriz asociada. stas pueden ser especificadas en cualquier momento del programa antes de dibujar la imagen. OpenGL mantiene una pila de matrices de transformacin que se han de aplicar sobre cada punto de la escena. Esta es una tcnica muy eficiente y til que exploraremos en futuros artculos. Por el momento vayamos al cdigo fuente, donde se definen algunas de estas transformaciones. En el fichero example2.cxx encontramos las ya familiares funciones reshape:
void mainReshape(int w, int h){ // VIEWPORT TRANSFORMATION glViewport(0, 0, w, h); // PROJECTION TRANSFORMATION glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(wnLEFT, wnRIGHT, wnBOT, wnTOP, wnNEAR, wnFAR); // MODELVIEW TRANSFORMATION glMatrixMode(GL_MODELVIEW); ....

La directiva glViewport(x, y, width, height) especifica la transformacin del Viewport: x, y son las coordenadas de la esquina inferior izquierda del rectngulo de la ventana de dibujo y width y height las dimensiones del viewport. Todos los nmeros se expresan en pixels. Entonces la funcin glMatrixMode(), utilizado para seleccionar la matriz actual, es invocada con los parmetros GL_PROJECTION para comenzar la especificacin de la transformacin de proyeccin. Antes de especificar cualquier transformacin de matrices es recomendable cargar la matriz unidad (que no hace nada sobre los vrtices de coordenadas), esto se hace con glLoadIdentity(), asigna la matriz unidad a la matriz actual. Luego viene la declaracin de la perspectiva 3D; la sentencia es glFrustum(left, right, bottom, top, near, far) declara los planos de recorte en las posiciones izquierda, derecha, abajo, arriba, cerca y lejos. Estos nmeros estn especificados en coordenadas del punto de vista (eye) y su magnitud determina la forma (la perspectiva) del volumen del espacio que se va a proyectar en el viewport (pantalla del ordenador). Quizs parezca complicado, a m me llevo un tiempo acostumbrarme. La mejor forma de entenderlo es experimentando con varios nmeros, recuerda siempre que has de elegir nmeros de forma que el objeto modelado-visto caiga dentro de los planos de recorte o no vers nada en la pantalla. Hay otras formas de especificar la transformada de proyeccin. Con el tiempo llegaremos a verlas. Finalmente cambiamos la matriz actual a la matriz del modelview, otra vez con la funcin glMatrixMode() y utilizando GL_MODELVIEW como parmetro. La funcin mainReshape() continua con otras cosas que no tienen nada que ver y acaba. Lo que importa es que despus de que la ventana principal ha sido re-dimensionada, esta funcin ha especificado el viewport y la transformada de proyeccin y finalmente establece como matriz actual, la matriz modelview. Lo que sucede luego es que la funcin mainDisplay() termina la especificacin del modelview y finalmente dibuja el polmero con scene():
void mainDisplay(){ glutSetWindow(winIdMain); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Limpiar los buffers de color y profundidad // Esto es como limpiar la pizarra. // Continuar con la transformacin MODELVIEW: // posicionar y orientar la cmara. glLoadIdentity(); // Cargar matriz unidad glTranslatef(0.0, 0.0, -4.0); // Mover la cmara 4 unidades atrs // dejamos la cmara apuntando en la direccin "-z". Realmente // esto funciona moviendo la siguiente escena 4 pasos en el eje "z". // Solo los elementos de la escena que queden dentro de el volumen // de representacin (ver transformacin de proyeccin ms adelante) // se vern en la pantalla.

// Dibujar el polmero glScalef(0.5, 0.5, 0.5); glRotatef(Angle, 0, 1, 0); scene(); }; glutSwapBuffers();

Espero no haber confundido mucho al lector por haber utilizado dos sub-ventanas. No estoy explicando cuestiones relativas a las sub-ventanas porque ya las expliqu en el artculo anterior ( Gestin de ventanas). Si tienes alguna duda, puedes ir a este artculo para refrescar la memoria. Esta funcin es muy simple. Primero glClear borra los buffers de color y profundidad. El buffer de profundidad es importante ahora en 3D porque la coordenada z de cada vrtice ha de ser comparada para determinar las superficies ocultas y eliminarlas. Despus cargamos la matriz unidad en la matriz actual modelview e invocamos las tres transformaciones de modelado:
glTranslatef(xt, yt, zt) , esto traslada el sistema de coordenadas actuales por el vector (xt, yt, zt). En nuestro caso, tiene el efecto de mover la cmara 4 unidades hacia atrs a lo largo de z, esto es, alejarse del modelo. Si no hacemos esto,

la cmara se quedara en el origen, en medio de la escena, con lo que difcilmente podramos ver nada. glScalef(xs, ys, zs) , como su nombre indica, su misin es la de escalar el sistema de coordenadas por los factores xs, ys, zs a lo largo de los ejes x, y, z respectivamente. Este escalado es necesario para que la escena expresada en coordenadas del mundo (polmero) quepa en las coordenadas del volumen visible. glRotatef(angle, vx, vy, vz) , gira el sistema actual de coordenadas a lo largo del vector normalizado (vx, vy, vz) el ngulo angle. Este es el truco que utilizamos para producir la ilusin de que la cmara gira al rededor de la escena. De hecho es la escena la que est girando. Hay muchas otras formas de mover la cmara, pero por el momento est es la ms sencilla.

Unas palabras de advertencia: el orden en el que se aplican las transformaciones de modelado es muy importante. Es necesario comprender qu es lo que sucede con la matriz de Modelview cada vez que invocas a una transformacin de coordenadas. Cada transformacin Ti se representa matemticamente por una matriz Mi. La superposicin de una secuencia de transformadas Tn Tn-1... T1 (por ejemplo: translacin + escalado + rotacin ) es representado matemticamente por una nica matriz M = Mn Mn-1 .... M1. El orden es crucial porque cuando la transformacin compuesta M acta sobre un vrtice v, las transformaciones son realmente aplicadas en el orden inverso: M v = Mn Mn-1 .... M1 v Primero M1, luego M2, etc.. y finalmente Mn. En nuestro cdigo ejemplo, he declarado la transformacin en el siguiente orden: translacin -> escalado -> rotacin. Por tanto, cada

punto del modelo en las coordenadas del mundo va a ser rotado -> escalado -> trasladado antes de ser proyectado sobre la pantalla grfica. Siempre has de tener este orden inverso de transformaciones en la cabeza cuando escribas cdigo, en caso contrario puedes obtener resultados no deseados muy sorprendentes. La funcin scene() sencillamente ejecuta el dibujado 3D (render) del objeto polmero. Para entender como se construye el modelo 3D, tenemos que ir al fichero Gd_opengl.cxx y echar un vistazo a la funcin miembro draw(GdPolymer &p). Hay un bucle principal que pasa por cada monmero de la cadena del polmero, obtiene sus coordenadas x,y,z dibuja una esfera en esa posicin, y luego dibuja los cilindros a lo largo de los enlaces que conectan cada monmero Recuerdas la primera pregunta? Aqu tenemos una posible solucin... Si encuentras otra ms rpida dmelo. Hay un cosa ms que el lector debe saber para entender completamente la rutina de dibujado del polmero Para qu sirven lasfunciones: glPushMatrix() y glPopMatrix()? Hay slo dos primitivas geomtricas en el modelo del polmero, una esfera de radio 0.40 centrada en el origen y un cilindro superior derecho de altura 1.0 y radio 0.4. El polmero se construye utilizando dos primitivas y una serie de transformaciones para situar las esferas y cilindros en las posicies correctas. Cada vez que se ejecutan las sentencias glCallList(MONOMER) o glCallList(CYLINDER) se dibuja una nueva esfera y cilindro en el origen. Para mover las esferas a las coordenadas x,y,z necesitamos una translacin (ver glTranslatef(x, y, z)); para dibujar y posicionar un cilindro es ms complicado porque tenemos que orientarlo en la direccin adecuada (en mi algoritmo utilizo una transformacin de escalado->rotacin). Pero cualquiera que sea el mtodo que utilices para construir modelos complejos 3D, no hay duda que necesitars varias transformaciones de translacin, rotacin y escalado. Cuando se invoca la funcin scene(), la matriz actual de la mquina de estados OpenGL es la matriz MODELVIEW, como he mencionado anteriormente, sta es la matriz que representa la proyeccin del modelo de coordenadas del mundo a coordenadas de recorte. ste es un problema serio, mientras la matriz MODELVIEW sea todava la matriz activa, cualquier nueva transformacin aplicada para construir el modelo 3D se aadir a la matriz actual, con la consecuencia indeseable de destruir la transformacin MODELVIEW. De forma similar, algunas veces queremos aplicar determinadas transformaciones 3D a una parte del modelo pero no a otras (por ejemplo, escalar un cilindro pero no la esfera). OpenGL resuelve este problema utilizando una pila interna de matrices. Hay dos operaciones bsicas sobre esta pila implementadas mediante glPushMatrix() (meter en pila) y glPopMatrix() (sacar de pila). Examina una vez ms el cdigo fuente de scene() y observars que antes de dibujar la esfera de cada nommero llamamos una vez a "push", para mover la matriz MODELVIEW a la pila, y al final del bucle llamamos a "pop" para restaurar la matriz MODELVIEW. El bucle interno que dibuja los enlaces del polmero tiene sus propios "push" y "pop" para aislar las transformaciones de escalado y rotacin de las translaciones que afectan a ambos, esfera y cilindro.

Hay mucho ms que decir sobre transformaciones 3D y pilas de matrices. En este artculo slo hemos araado la superficie de estas cuestiones. Por el momento lo dejaremos as y dejaremos que el lector interesado explore el cdigo fuente de la demo e intente construir sus propios modelos 3D. El cdigo example2 tambin utiliza unas cuantas caracterstica aun no estudiadas: materiales e iluminacin. Dejamos la presentacin de estos temas para artculos futuros. La prxima vez, continuaremos explorando con mayor profundidad las transformaciones 3D y las pilas de matrices, tambin mostraremos como utilizar ambas caractersticas de OpenGL para implementar un robot mvil. Hasta entonces, pasatelo bien con OpenGL.

Primeros pasos en OpenGL


Este tutorial cubre los primeros conceptos previos a la creacin de aplicaciones con OpenGL y en general a la creacin de contenido 3d. Es necesario aclarar que el tutorial, por simplicidad y extensin se enfoca sobre la especificacin 1.1 de OpenGL, es decir, solo se menciona la funcionalidad de la llamada Fixed Pipeline (ahora con la entrada de OpenGL 2.0 y la introduccin de los shader al estndar, la estructura cambio). En el tutorial se mencionan algunas palabras en ingles, acompaadas de su traduccin al espaol, solo para conservar el trmino original (en ingles) y facilitar la bsqueda posterior del trmino, tanto en espaol, como en ingls. Qu es OpenGL? : OpenGL (Open Graphics Library) es un API (de Aplication Programming Interface, Interfaz de Programacin de Aplicaciones) libre, multiplataforma, orientado a la creacin de contenido 3d. A un nivel ms simple se puede decir que es una librera que permite hacer software con grficos 3d sin preocuparse por el hardware en que se va a ejecutar y que tiene un gran soporte en diferentes sistemas operativos como Windows, OS/2, UNIX, FreeBSD , Linux, etc. Hay que hacer una diferencia clara, OpenGL no es un lenguaje de programacin, pero se puede trabajar con l desde diferentes lenguajes como c+ +, c#, java y Visual Basic. Para que puedo usar OpenGL? : OpenGL puede ser usado para todo tipo de aplicaciones que necesiten visualizacin 3d en tiempo real de alto rendimiento y calidad, en esto estn incluido los videojuegos, CAD, aplicaciones cientficas, mdicas, militares, simuladores, etc. Qu necesito para utilizar OpenGL en mis programas? Es buena idea utilizar un IDE como Visual Studio, Eclipse o DevCpp para realizar los programas, ya que facilita mucho el trabajo, adicionalmente, en los archivos fuente donde se utilicen funciones de OpenGL es necesario incluir las bibliotecas de prototipos gl.h y glu.h, que generalmente se encuentran preinstaladas no solo en Visual Studio, sino en otros entornos donde se soporta el uso de OpenGL. Tambin es necesario hacer un link con las libreras binarias, en Visual Studio las dos cosas se veran como:
PLAIN TEXT
C++:

1. 2. 3. 4. 5. 6. 7. 8. 9.

//Inclusin de las libreras prototipo #include <gl/gl.h> #include <gl/glu.h> //Link con las libreras binarias #pragma comment(lib, opengl32.lib) #pragma comment(lib, glu32.lib)

Es importante notar 2 cosas, las libreras binarias que estamos usando corresponden a la implementacin de software de Microsoft (el 32 al final lo indica), que finalmente utilizan las libreras dinmicas opengl32.dll y glu.dll localizados en el directorio de Windows y por otra parte, no todos los IDE y sistemas operativos obedecen a la misma forma de inclusin de OpenGL, por lo que el desarrollo variar ligeramente en este punto, para mayor informacin al respecto ver http://nehe.gamedev.net/. Con estas pequeas aclaraciones, es necesario pasar a algunos conceptos fundamentales del 3d. SISTEMAS DE COORDENADAS Para trabajar grficos bien sea 2d o 3d es necesario tener una forma de describir la posicin de un objeto cualquiera en el espacio. Para esto es necesario establecer un patrn de referencia, lo cual se hace generalmente a travs de un sistema de coordenadas y el sistema al que mas estamos acostumbrados es el de coordenadas cartesianas, en el cual un punto puede ser expresado como una distancia a travs uno (x), dos (x,y) o tres ejes (x,y,z) mutuamente perpendiculares que se intersecan en el punto (0,0) en el caso de dos dimensiones y (0,0,0) en el caso de tres.

OpenGL maneja los sistemas 2d y 3d, aunque el sistema de 2d sobre pxeles en pantalla es bastante ineficiente, por lo que en la mayora de los casos mejor utilizar 3d y simular el 2d con una proyeccin ortogrfica de la escena.

Primeros pasos en OpenGL


Si bien la forma en que se representan las coordenadas cartesianas en matemticas es bastante estndar (x positivo a la derecha y y positivo arriba), en los sistemas de dibujo en el computador hay variaciones dependiendo del sistema de dibujo sobre el cual se est trabajando y el caso del 3d es totalmente dependiente de desde donde, hacia donde y que es arriba. Esto quiere decir que las coordenadas del sistema abstracto no tienen que ser concordantes con el sistema de coordenadas sobre la pantalla, por lo que se deber realizar un proceso llamado mapeo de coordenadas. En este caso el mapeo debe hacerse del sistema abstracto de coordenadas de OpenGL al sistema de coordenadas en ventana del sistema operativo (window, UNIX, linux, mac, etc). El siguiente esquema muestra tres posibles formas de realizar el mapeo de coordenadas:

El grafico muestra tres de las posibles formas de realizar el mapeo las coordenadas. En el Modo 1, el punto (0,0) en coordenadas de dibujo es el correspondiente al extremo inferior izquierdo de la ventana y el punto (200,200) estar en el extremo superior derecho. Tal vez es uno de los modos que parece ms natural En el Modo 2, el punto (0,0) en coordenadas de dibujo es el centro de la ventana y los puntos extremos estarn situados en los cuatro cuadrantes, en (100,100) superior derecha, (-100,100) superior izquierda, (-100.-100) inferior izquierda y (100,-100) inferior derecha. Este modo es similar a la forma en que lo muestra la cmara por defecto de OpenGL, con el agregado del eje z positivo saliendo de la pantalla. En el Modo 3, el punto (0,0) en coordenadas de dibujo se encuentra en el extremo superior izquierdo de la ventana y el punto (200,200) corresponde al extremo inferior derecho. Hay que notar, que en este modo el eje y positivo se encuentra cabeza abajo, es decir, cuando un punto aumenta su coordenada y va hacia abajo, no hacia arriba. Este modo es usado generalmente por los dispositivos de hardware y es la forma por defecto de GDI. Estas son solo algunas formas de mapeo de coordenadas y en cualquier caso y con un poco de matemticas (matrices de transformacin) se puede pasar de cualquier modo de mapeo a otro. PROYECCION Los monitores actuales de PC, los televisores, las pantallas de PDA, de celulares y consolas porttiles son todos dispositivos bidimensionales, la percepcin 3d sobre estos dispositivos se debe a un conjunto de trucos para engaar al ojo humano y hacerle ver objetos tridimensionales sobre un dispositivo bidimensional, el primero de estos trucos es la proyeccin. La proyeccin consiste en generar una imagen bidimensional de una escena tridimensional, algo as como sacarle una foto, o pintar un cuadro de la escena. Tal vez las

dos mas conocidas y las que permite trabajar OpenGL directamente son la ortogrfica y la perspectiva. Proyeccin Ortogrfica: En este tipo de proyeccin se trazan rayos paralelos al plano de proyeccin (son paralelos porque se considera la fuente de luzo centro de proyeccin en el infinito) y la imagen se forma con aquellos rayos que intersequen al objeto. Esto se ve mas claro en el diagrama.

Las imgenes producidas por este tipo de proyeccin se ven como si el objeto se hubiera aplanado sobre el plano de proyeccin (que puede ser tomado como la pantalla), es decir, no existe sensacin de profundidad, ya que variando la distancia del objeto al plano de proyeccin, no variar el tamao de la imagen producida por este. Este tipo de proyeccin es usada en aplicaciones CAD porque permite ver el modelo desde diferentes partes (bien sea rotando el objeto o la cmara) sin que se deformen las dimensiones en la proyeccin. Una forma de dibujo 2d basada en esta proyeccin es utilizada en los videojuegos, se trata de los juegos con perspectiva isomtrica donde se conservan parte de las medidas, al igual que las lneas paralelas. Tambin es posible realizar juegos 2d utilizando esta perspectiva, ya que los grficos se ven planos sobre la pantalla porque su dimensin es independiente de su profundidad con respecto al plano de proyeccin.

Primeros pasos en OpenGL


Proyeccin en Perspectiva: La fuente de rayos en este caso no se encuentra en el infinito, sino que es un punto localizado llamado punto de fuga. Los rayos que parten del punto de fuga e intersecan al objeto son entonces los que formaran la imagen sobre el plano de proyeccin.

En la proyeccin en perspectiva los objetos sufren una deformacin por la distancia, a medida que los objetos se alejan del plano de proyeccin disminuyen su tamao justo de la forma en que ocurre cuando un objetos se aleja de nuestros ojos ( en la figura se muestra como los dos objetos a pesar de ser de diferentes tamaos producen una imagen casi del mismo tamao sobre el plano de proyeccin, debido a la diferencia de distancias), es por eso, que esta perspectiva es la mas utilizada para dar una sensacin de realismo, como la que se necesitan en los juegos, simulaciones y animaciones. En el caso de OpenGL, tanto en el modo de perspectiva como en el ortogrfico se limita la regin del espacio sobre la que se realizan los clculos, a esta regin se le llama volumen de vista o volumen de recorte (clipping volume) y en el caso de la perspectiva tambin se la llama frustrum por la forma que tiene. Dicha regin se especifica, ya que los objetos por fuera de este campo a derecha o izquierda no aparecern en pantalla y aquellos muy lejanos generarn pocos rasgos, por lo que de calcularse consumiran recursos innecesariamente (la consideracin de lejano depende del programador).

En el caso de la proyeccin ortogrfica el volumen de vista tiene forma de caja rectangular, por lo que se determina fcilmente mediante 6 medidas: near, far, top, bottom, left, right (cerca, lejos, arriba, abajo, izquierda, derecha) y en el caso de proyeccin, tiene forma de pirmide truncada de base rectangular, que puede ser determinada por el field of view (campo de visin), el aspect ratio ( el alto del plano mas cercano dividido el ancho), near y far.

Hay que anotar que ambos volmenes se calculan a partir de la posicin del observador, que en el caso de OpenGL es la cmara. Tambin hay que observar que las dimensiones del plano ms cercano a la cmara pueden ser mayores o menores a las de la ventana, por lo el mapeo de las condenadas no ser con relacin uno a uno, adicionalmente OpenGL permite definir el

viewport (vista), es decir la regin sobre la ventana en la que se va a realizar el dibujado, que puede ser menor al tamao de la ventana. Hasta este punto el tutorial ha mostrado algunos conceptos bsicos relacionados con la forma en que OpenGL trabaja los grficos tridimensionales, conceptos que posteriormente se vern reflejados en las primeras aplicaciones que se realicen con OpenGL. Es importante tomar en cuenta que un buen manejo del lgebra lineal, los TDA como las pilas y conocimientos en un lenguaje de programacin como c++ permitirn avanzar ms rpidamente en el aprendizaje del API OpenGL

Hola mundo 3d en OpenGL


Este tutorial aborda la creacin una aplicacin simple en OpenGL de dos formas diferentes, a travs de las libreras aux y glut. Como ejemplos estilo Hola Mundo estn orientadas a mostrar en pantalla aplicaciones simples que introduzcan al uso de una tecnologa nueva para alguien, en este caso OpenGL, por esto mismo no se explican en profundidad, extensin o detalle todos los conceptos relacionados con el cdigo fuente. Los dos programas fueron probados en el Visual Studio .net 2003, pero es de esperar que funcionen en versiones anteriores (2002, 6) y en entornos diferentes (devcpp). En otros lenguajes como Java o c#, el cdigo variar dependiendo de la sintaxis del lenguaje, la forma en que se realice el binding (ligadura) con el API (OpenGL esta escrito orientado al lenguaje c) y el sistema operativo. Primera Aplicacin 3d Usando AUX En los comienzos de OpenGL aux (de auxiliar) se creo como una librera para ayudar a los programadores a entender el uso del API, hacer ejemplos y prototipos de manera rpida sin tener que preocuparse por las particularidades del sistema operativo. Actualmente aux esta descontinuada, no es mantenida y en la pgina oficial de OpenGL no se recomienda utilizarla, pero est en este tutorial de manera ilustrativa.
PLAIN TEXT
C++: 1. #include <windows.h> 2. #include <stdio.h> 3. #include <gl\gl.h> 4. #include <gl\glu.h> 5. #include <gl\glaux.h> 6. 7. #pragma comment(lib, "opengl32.lib") 8. #pragma comment(lib, "GlAux.Lib") 9. #pragma comment(lib, "glu32.lib") 10. 11. void main(void) 12. { 13. auxInitDisplayMode(AUX_SINGLE | AUX_RGBA);

14. 15. 16. 17. 18. 19. 20. 21. 22. }

auxInitPosition(100,100,640,480); auxInitWindow("Hola Mundo 3d"); glViewport(0,0,640,480); gluPerspective(45.0f,640.0f /480.0f, 1.0 ,150.0f); glTranslatef(0,0,-15.0f); auxWireTeapot(4.0f); glFlush(); getchar();

Con estas 9 lneas de cdigo (sin contar las inclusiones y la declaracin del main) se muestra una consola y una ventana con una tetera dibujada en alambres, para cerrar la ventana es necesario introducir una enter en la consola.

Las primeras 5 lneas de cdigo son las inclusiones de las liberis que necesita el programa, son windows.h , requeridas por OpenGL, stdio.h para la funcin getchar() y las de OpenGL gl.h, glut.h y glaux.h. Todas estas libreras vienen en la distribucin de Visual C++, por lo que no hay que preocuparse de su ubicacin en el disco duro. Las siguientes 3 lneas son las directivas #pragma comment(lib, "...") sirven para ligar las libreras binarias de OpenGL, ya que las .h solo contienen los prototipos de las funciones que se utilizarn. Al igual que las cabeceras .h, estas vienen en la distribucin de Visual c++.

La siguiente lnea es la declaracin de la funcin main(), que identifica al programa como de tipo consola. Es importante elegir este tipo de plantilla al crear el proyecto, ya que de otra manera la configuracin del compilador no ser la adecuada para que el programa se compile y ejecute. Finalmente llegamos a las lneas que constituyen propiamente el programa

auxInitDisplayMode(AUX_SINGLE | AUX_RGBA) : Esta funcin realiza dos inicializaciones el rea de dibujo de la ventana, la primera AUX_SINGLE, indica un buffer sencillo, la segunda AUX_RGBA indica el modo de colores deseado, es decir los canales, Red (Rojo) Green (Verde) Blue (Azul) Alpha (Transparencia). auxInitPosition(100,100,640,480): los dos primeros valores indican la posicin de la ventana cuando se muestre y los dos ltimos indican el tamao de la ventana. Asi que esta linea configura una pantalla que se muestra en (100,100) contados desde la esquina superior izquierda de la pantalla (x + derecha, y + abajo) y de tamao 640x480. auxInitWindow("Hola Mundo 3d): inicializa el ttulo de la ventana, que es el que aparece en la barra superior de esta, en este caso es Hola Mundo 3d. glViewport(0,0,640,480): Inicializa el rectngulo sobre el que se va a dibujar en la ventana, los parmetros son posicin (x,y) contados a partir de la esquina inferior izquierda (x + derecha, y + arriba) y las dimensiones ancho alto. En este caso el recuadro de dibujo empieza en (0,0) y tiene dimensiones 640x480 (el tamao completo de la ventana) gluPerspective(45.0f,640.0f /480.0f, 1.0f ,150.0f) Define el frustrum. Los parmetros son el ngulo de visin (45.0), el aspecto que es el ancho de la ventana dividido el alto (640.0f/480.0) y la distancia mas cercana (1.0) y lejana (150.0) dentro de la cual pueden estar los objetos antes de ser recortados. Si parte de un objeto est recortado, es parte no se dibuja. glTranslatef(0,0,-15.0f): Sirve para trasladar el objeto los objetos (la tetera) una distancia (x,y,z) a partir de donde estn, en este caso (0,0,15.0f) a partir del origen. auxWireTeapot(4.0f): Dibuja una tetera de tamao 4.0 en la posicin actual (0,0,-15.0) glFlush(): Obliga a los comandos pendientes de OpenGL a ejecutarse, en este caso, mueve y dibuja la tetera. getchar(): Esta lnea est por facilidad. De no estar incluida, la ventana aparecera y desaparecera (cuando se terminen de ejecutar todas las instrucciones) o se tendra que agregar unas cuantas lneas de cdigo para que el programa funcionara mas como una aplicacin convencional.

A cambio de tener pocas lneas, el Hola Mundo 3d con aux tiene pocas prestaciones y aunque se podra mejorar, es mejor seguir con glut.

Hola mundo 3d en OpenGL


Primera Aplicacin 3d Usando GLUT

GLUT es el GL Utility Toolkit (herramientas utilitarias GL), desarrollada originalmente por Mark J. Kilgard como un reemplazo para la librera aux, con funcionalidades adicionales, mas estable y con un rango mas amplio de sistemas operativos soportados. Posteriormente la librera fue tomada por otros como Nate Robins quienes realizaron modificaciones y mejoras a la librera inicial.
PLAIN TEXT
C++: 1. #include <windows.h> 2. #include <gl\gl.h> 3. #include <gl\glu.h> 4. #include "glut.h" 5. 6. 7. #pragma comment(lib, "opengl32.lib") 8. #pragma comment(lib, "glut32.lib") 9. #pragma comment(lib, "glu32.lib") 10. 11. void render(void) 12. { 13. glClear(GL_COLOR_BUFFER_BIT); 14. glLoadIdentity(); 15. glTranslatef(0.0f,0.0f,-15.0f); 16. glutWireTeapot(4.0f); 17. glutSwapBuffers(); 18. } 19. 20. void Redimensionar(GLsizei ancho, GLsizei alto) 21. { 22. if(alto == 0) 23. { 24. alto = 1; 25. } 26. 27. glViewport(0, 0, ancho, alto); 28. glMatrixMode(GL_PROJECTION); 29. glLoadIdentity(); 30. if(ancho>alto) 31. { 32. gluPerspective(45.0f,ancho/alto,1,150.0f); 33. } 34. else 35. { 36. gluPerspective(45.0f,alto/ancho,1,150.0f); 37. } 38. 39. glMatrixMode(GL_MODELVIEW); 40. glLoadIdentity(); 41. }

42. void main(void)


43.

44. 45. 46. 47. 48. 49. }

{ glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); glutCreateWindow("Hola Mundo 3d"); glutDisplayFunc(render); glutReshapeFunc(Redimensionar); glutMainLoop();

Ahora son 21 lneas de cdigo (sin contar de nuevo las inclusiones ni las declaraciones de funciones), pero ahora la ventana es capaz de responder a los comandos ms simples del sistema operativo como cambiar de tamao, moverse y cerrarse. En la pantalla se vera algo como

Las nuevas libreras utilizadas son las de glut.h y glut32.lib, que no vienen incluidas en la distribucin del Visual Studio, por lo que es necesario incluirlas manualmente bien sea en el compilador y/o en el disco duro (basta colocarlas en la carpeta del proyecto) y en Windows adicionalmente hay que proporcionar las libreras dinmicas glut32.dll si se usa opengl32.lib, la distribucin de Microsoft o glut.dll si se usa opengl.lib, la distribucin de SGI. Para proporcionar las libreras dinmicas se pueden colocar en la carpeta donde se ejecute el programa o copiarlas a la carpetas WINDOWS/system32/, esta ltima es mejor si se van a desarrollar varias aplicaciones dependientes de esta librera. Las lneas de cdigo nuevas son: En la funcin render() que se encarga de dibujar los elementos.

glClear(GL_COLOR_BUFFER_BIT): Indica que se limpie un buffer con el color actual de limpiado (el color por defecto es negro) glLoadIdentity(): Carga la matriz identidad sobre la matriz actual (modelview), es necesario hacerlo ya que la funcion render() se ejecuta muchas veces, lo que hara que el glTranslate fuera acumulativo si no se cargara la matriz identidad. glutSwapBuffers(): Cambia el buffer actual sobre el que se est dibujando, por defecto el buffer sobre el que se dibuja es el buffer de atrs y el que se muestra es el del frente. En la funcin Redimensionar() que se encarga de cambiar el viewport y el aspecto de la escena cuando el usuario cambia de tamao la ventana: if(alto == 0){ alto = 1;}: Previene que el alto sea 0, para evitar posteriormente un error de divisin entre 0. glMatrixMode(GL_PROJECTION); glLoadIdentity(): Cambia el modo de matriz a proyeccin y luego carga la identidad sobre esta matrz. if(ancho>alto){ gluPerspective(45.0f,ancho/alto,1,150.0f);}: Define el frustrum, en este caso el alto es mayor que el ancho, as que el aspecto es ancho/alto. else{gluPerspective(45.0f,alto/ancho,1,150.0f);} En este caso el aspecto es alto/ancho, ya que la el alto es mayor que el ancho. glMatrixMode(GL_MODELVIEW); glLoadIdentity(); Se elige la matriz MODEL_VIEW y se carga la identidad sobre ella.

Por ultimo este programa tambin parte del modo consola (como lo muestra la consola de comandos que aparece cuando se ejecuta), las lneas de cdigo en el main() son:

glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB): Inicializa la ventana con modo doble buffer (uno sobre el que se pinta y otro que se muestra en pantalla) y solicita un modo RGB de colores. glutCreateWindow("Hola Mundo 3d"): El titulo de la ventana, en este caso Hola Mundo 3d. glutDisplayFunc(render); Indica a la librera glut cual ser la funcin que realizar el pintado sobre OpenGL, en este caso es render(), que ya se haba definido anteriormente. glutReshapeFunc(Redimensionar): Indica a glut la funcin que ser llamada cuando el usuario cambie el tamao de la ventana. En este caso la funcin es Redimensionar() que se haba definido anteriormente. glutMainLoop(): Finalmente esta funcin es el ciclo de glut, donde se realizan la recepcin, procesamiento de mensajes del sistema operativo y el dibujado sobre el rea OpenGL.

Hola Complicado Mundo OpenGL-Win32


Este tutorial aborda la creacin de una ventana en Windows con un contexto grfico OpenGL, pero a diferencia de los tutoriales basados en GLUT o AUX, esta aplicacin se desarrollar a partir de las libreras de Windows, haciendo todo manualmente. Surge la inquietud de por qu hacer este tutorial, si podra hacerse ms fcilmente con GLUT, AUX o cualquier otra librera?, la respuesta es porque este tutorial permite conocer mejor la estructura de un programa de Windows, que es til si se necesita algo mas de control (y

problemas) sobre la aplicacin. Adems, es divertido ensuciarse un poco las manos con el API de Windows de bajo nivel. PROGRAMANDO ORIENTADO A EVENTOS Windows y en general los sistemas operativos GUI utilizan una arquitectura orientada a eventos para manejar la interaccin usuario sistema aplicacin de manera organizada y eficiente. El modelo es bastante simple, cada vez que el usuario realiza una accin, el sistema determina sobre que aplicacin se realiz y le enva un mensaje describiendo la accin, el programa lee y procesa el mensaje, si es que este le interesa (un clic sobre el men por ejemplo) y realiza los cambios pertinentes. Un ejemplo con un clic podra ser como este:

En este caso la aplicacin, por ejemplo un juego de estrategia, averiguara la posicin en la que en la que se encontraba el puntero cuando se hizo el clic y movera una unidad a esta posicin. Es importante anotar que este modelo fue creado para ser eficiente en uso de recursos cuando varias aplicaciones corren simultneamente, un videojuego por el contrario es una aplicacin destinada a consumir recursos por su naturaleza de aplicacin en tiempo real, la razn de esto es que aunque el usuario se tome una siesta, el videojuego est realizando clculos, reproduciendo sonidos, dibujando la pantalla, la IA esta intentando conquistar el mundo, etc. LA APLICACION Las siguientes estructuras y tipos de datos sern usados en el desarrollo de la aplicacin:

HDC: Handle to Device context. Es el identificador de un Device Context. Un Device Context es una aplicacin a travs de la cual tienen que pasar las operaciones de dibujo, para que estas puedan ser mostradas en un dispositivo fsico, en este caso particular un monitor. PFD: Pxel Format Descriptor. Estructura que describe las propiedades de los pxeles en una superficie de dibujado. HWND: Handle to Window. Es el identificador de una ventana. WNDCLASS: Estructura que contiene los datos necesarios para el registro de una clase ventana, como son su nombre, estilo, funcin de paso de mensajes, icono, cursor, entre otros. HGLRC: Handle to GL Render Context. Es el identificador de un Render Context, similar al HDC, solo que este no es usado por GDI sino por OpenGL para realizar el dibujado sobre la pantalla.

Es posible dividir esta aplicacin en 4 tareas principales:


Crear la ventana win32. Preparar la ventana y ligarla a OpenGL. Inicializar OpenGL. Hacer el ciclo de mensajes y dibujado.

Las macros y variables del programa son:


PLAIN TEXT
C++: 1. #define VENTANA_ANCHO 640 2. #define VENTANA_ALTO 480 3. 4. HDC hdc=NULL; 5. HGLRC hglrc=NULL; 6. HWND hWnd=NULL; 7. HINSTANCE hInstance;

Hola Complicado Mundo OpenGL-Win32


CREAR LA VENTANA WIN32 Para crear la ventana es necesario llenar el WNDCLASS, registrarlo y finalmente crear la ventana. El WNDCLASS
PLAIN TEXT
C++: 1. WNDCLASS 2. 3. hInstance Cwnd; = GetModuleHandle(NULL);

4. Cwnd.style = CS_OWNDC; 5. Cwnd.lpfnWndProc = (WNDPROC) WndProc; 6. Cwnd.cbClsExtra = 0; 7. Cwnd.cbWndExtra = 0; 8. Cwnd.hInstance = hInstance; 9. Cwnd.hIcon = NULL; 10. Cwnd.hCursor = LoadCursor(NULL, IDC_ARROW); 11. Cwnd.hbrBackground = NULL; 12. Cwnd.lpszMenuName = NULL; 13. Cwnd.lpszClassName = "OpenGL";
14. 16.

15. RegisterClass(&Cwnd); 17. hWnd=CreateWindow( 18. "OpenGL", 19. "Hola Mundo",


20.

21. 24. 25. 27.

22. 23.

26.

WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN ,100, 100, VENTANA_ANCHO, VENTANA_ALTO, NULL, NULL, hInstance, NULL);

La funcin GetModuleHandle() obtiene un identificador para la aplicacin actual. Los datos de la estructura WNDCLASS son los siguientes: WNDCLASSS { UINT style : Estilo de la aplicacin, en este caso se quiere un DC propio (CS_OWNDC) WNDPROC lpfnWndProc : Manejador de mensajes int cbClsExtra : Bytes extra para la estructura int cbWndExtra : Bytes extra para la clase HINSTANCE hInstance : Instancia de la aplicacin que crea la ventana HICON hIcon : Icono de la ventana, al ser NULL, windows provee el icono por defecto. HCURSOR hCursor : Cursor usado al pasar sobre la ventana, LoadCursor(NULL,IDI_ARROW) es el icono por defecto HBRUSH hbrBackground : Color del fondo de la ventana,debido a que todo el espacio de la ventana va a estar ocupado por la ventana, este valor se provee como NULL LPCTSTR lpszMenuName : Nombre del recurso que identifica el men de la aplicacin, es NULL ya que no se utiliza men. LPCTSTR lpszClassName : Nombre de la clase que se usara al registrarla. } El manejador de mensajes al que hace referencia es en este caso uno muy simple:

PLAIN TEXT
C++:

1. LRESULT CALLBACK WndProc(HWND

hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam) 2. { 3. switch (uMsg) 4. { 5. case WM_CLOSE: 6. { 7. PostQuitMessage(0); 8. return 0; 9. } 10. } 11. return DefWindowProc(hWnd,uMsg,wParam,lParam); 12. }

Las entradas de esta funcin son: HWND hWnd : Identificador de la ventana que recibe el mensaje UINT uMsg : El mensaje WPARAM wParam : Informacin adicional sobre el mensaje LPARAM lParam : Informacin adicional sobre el mensaje Esta funcin solo intercepta un mensaje, aquel que es enviado por el sistema para indicar a la aplicacin que debe cerrarse (WM_CLOSE) y si es cualquier otro mensaje lo enva al DefWindowProc(). RegisterClass() registra una clase ventana, con los parmetros dados por un WNDCLASS, en este caso CWnd. Es hora de crear una ventana a partir de la clase creada con el RegisterClass(), para esto se utiliza la funcin CreateWindow() cuyos parmetros son los siguientes: HWND CreateWindow ( LPCTSTR lpClassName : Nombre de la clase a partir de la cual se crea la ventana LPCTSTR lpWindowName : Titulo de la ventana. DWORD dwStyle : Estilo de la ventana, en este caso con titulo y borde (WS_OVERLAPED) int x, : Posicin x de la ventana int y : Posicin y de la ventana int nWidth : Ancho de la ventana int nHeight : Alto de la ventana HWND hWndParent : Identificador de la ventana padre, en este caso NULL, ya que no tiene HMENU men : Identificador del men de la ventana, en este caso NULL, ya que no tiene

HINSTANCE hInstance : Identificador de la aplicacin poseedora de la ventana, en NT/2000/XP este valor es ignorado LPVOID lpParam : Cadena con el parmetro a pasar al llamar el mensaje WM_CREATE. En este NULL, ya que no hay ningun parmetro a pasar )

Hola Complicado Mundo OpenGL-Win32


PREPARAR LA VENTANA Y LIGARLA A OPENGL Ahora es necesario:
PLAIN TEXT
C++: 1. GLuint PixelFormat; 2. 3. static PIXELFORMATDESCRIPTOR pfd= 4. { 5. sizeof(PIXELFORMATDESCRIPTOR), 6. 1, 7. PFD_DRAW_TO_WINDOW |PFD_SUPPORT_OPENGL |PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, 8. 32, 9. 0, 0, 0, 0, 0, 0, 10. 0, 11. 0, 12. 0, 13. 0, 0, 0, 0, 14. 32, 15. 0, 16. 0, 17. 0, 18. 0, 19. 0, 0, 0 20. }; 21. 22. hdc=GetDC(hWnd); 23. PixelFormat=ChoosePixelFormat(hdc,&pfd); 24. SetPixelFormat(hdc,PixelFormat,&pfd); 25. hglrc=wglCreateContext(hdc); 26. wglMakeCurrent(hdc,hglrc);

El PIXELFORMATDESCRIPTOR tiene los siguientes datos: PIXELFORMATDESCRIPTOR { WORD nSize : Tamao de la estructura WORD nVersion : Versin de la estructura, siempre es 1

DWORD dwFlags : Propiedades del buffer de pxeles, en este caso dibujar sobre una ventana (PFD_DRAW_TO_WINDOW), soporte a OpenGL (PFD_SUPPORT_OPENGL) y doble buffer (PFD_DOUBLEBUFFER) BYTE iPixelType : La forma de describir los pxeles, en este caso como RGBA (PFD_TYPE_RGBA) BYTE cColorBits : Tamao de un pxel individual en bits, en este caso 32 que es el mas comn BYTE cRedBits : Ignorado BYTE cRedShift : Ignorado BYTE cGreenBits : Ignorado BYTE cGreenShift : Ignorado BYTE cBlueBits : Ignorado BYTE cBlueShift : Ignorado BYTE cAlphaBits : Ignorado BYTE cAlphaShift : Ignorado BYTE cAccumBits : Ignorado BYTE cAccumRedBits : Ignorado BYTE cAccumGreenBits : Ignorado BYTE cAccumBlueBits : Ignorado BYTE cAccumAlphaBits : Ignorado BYTE cDepthBits : Bits para el buffer de profundidad, pueden ser 16 o 32 como en este caso. BYTE cStencilBits : Ignorado BYTE cAuxBuffers : Ignorado BYTE iLayerType : Ignorado BYTE bReserved : Ignorado DWORD dwLayerMask : Ignorado DWORD dwVisibleMask : Ignorado DWORD dwDamageMask : Ignorado } GetDC() obtiene el Device Context asociado a la ventana identificada por hWnd. ChoosePixelFormat() utiliza dos parmetros, el hdc sobre el cual se buscara un formato de pxeles compatible y el pfd que especifica el formato deseado por la aplicacin. Esta funcin devuelve el formato de pxeles mas cercano encontrado, con respecto al pfd. SetPixelFormat() utilizar tres parmetros, el hdc sobre el cual se va a establecer el formato de pxeles, un entero, el PixelFormat, donde esta guardado el ndice a dicho formato de pxeles y el pfd de referencia que se uso para encontrar el formato, el ultimo valor no afecta el funcionamiento de esta funcin, ya que se utiliza solamente como registro. wglCreateContext() crea un contexto de dibujo para OpenGL, a partir de un contexto de dibujo de GDI.

wglMakeCurrent() elige un HGLRC sobre el cual se va a dibujar los OpenGL, esto es debido a que una aplicacin puede tener varios contextos de dibujo, pero solo puede operar un proceso sobre un contexto a la vez. INICIALIZAR OPENGL La inicializacin de OpenGL es en este caso bastante simple y ms bien utilitaria, es realizada por las siguientes lneas de cdigo:
PLAIN TEXT
C++:

1. 2. 3. 4. 5. 6. 7.

glClearColor(0.0f, 0.0f, 0.0f, 0.5f); glMatrixMode (GL_PROJECTION); glViewport (0, 0, VENTANA_ANCHO , VENTANA_ALTO); gluPerspective(45.0f,VENTANA_ANCHO/VENTANA_ALTO,0.1f,100.0f); gluLookAt( 0,0,16, 0,0,0, 0,1,0); glMatrixMode (GL_MODELVIEW); glLoadIdentity();

glClearColor() establece el color de fondo glMatrixMode() escoge una matriz para operar sobre ella. Primero se elige la de proyeccin (GL_PROJECTION) glViewport() establece el tamao del marco de dibujo de OpenGL gluPerspective() establece el frustrum, en este caso apertura de 45, aspecto de 640/480, plano mas visible a 0.1 de distancia y mas lejano a 100. gluLookAt() posiciona la cmara (en realidad hace transformaciones sobre la matriz proyeccin. Los parmetros estn agrupado de a 3 (x, y, z) y representan la posicin de la camara, el punto al que mira y que vector es considerado hacia arriba Finalmente se vuelve al modo de matriz GL_MODELVIEW que es el utilizado para dibujar las primitivas. HACER EL CICLO DE MENSAJES Y DIBUJADO El ciclo de mensajes se encarga de revisar constantemente los mensajes, en busca de alguno de inters para la aplicacin, de no haber mensajes, se dibuja la escena de OpenGL. El cdigo es as:
PLAIN TEXT
C++: 1. MSG mensaje; 2. BOOL continuar=TRUE; 3. 4. while(continuar) 5. {

6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23.

if (PeekMessage(&mensaje,NULL,0,0,PM_REMOVE)) { if (mensaje.message==WM_QUIT) { continuar=FALSE; } else { TranslateMessage(&mensaje); DispatchMessage(&mensaje); } } else { render(); SwapBuffers(hdc); } }

La variable continuar indica cuando el ciclo debe detenerse, cuando el usuario o el sistema decidan cerrar la aplicacin, bien sea con la X sobre la barra de la ventana, con el mtodo abreviado, mediante el administrador de aplicaciones, etc. En algunos ejemplos se obvia esta variable al hacer un ciclo while(1) y utilizando un break; en caso de recibir el mensaje. PeekMessage() busca y almacena un mensaje de la pila de mensajes de la aplicacin. Si tal mensaje existe lo almacena en el primer parmetro y devuelve TRUE, de lo contrario devuelve FALSE. El ultimo parmetro (PM_REMOVE) le indica a la funcin al leer un mensaje debe borrarlo de la pila de mensajes. En este caso solo estamos observando el mensaje WM_QUIT, que indica que el WndProc llamo la funcin PostQuitMessage(), es decir que intercepto algn mensaje de salida de la aplicacin. Si no es este mensaje, se vuelve a colocar en la pila de mensajes mediante las funciones TranslateMessage() y DispatchMessage, para que este pueda ser ledo por el wndProc() creado anteriormente o por el manejador de mensajes por defecto (DefWindowProc()). Finalmente si no hay ningn mensaje por leer en la pila de mensajes, se hace el dibujado(render()) y se cambia de buffer (SwapBuffers()). El cdigo del render() es bastante simple y pinta un triangulo
PLAIN TEXT
C++:

1. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
2.

3. glBegin(GL_TRIANGLES);

4. 5. 6. 7. 8.

glColor3f(0.0f,0.0f,1.0f); glVertex3f(3.0f,-2.0f,0.0f); glVertex3f(0.0f,1.0f,0.0f); glVertex3f(-3.0f,-2.0f,0.0f); glEnd();

glClear() limpia los buffers pasados como parmetro de acuerdo al color de limpiado (negro) y entre las funciones glBegin() y glEnd() esta la especificacin de un triangulo de color azul.

Hola Complicado Mundo OpenGL-Win32


El cdigo completo del programa,de 148 lneas de cdigo es:
PLAIN TEXT
C++: 1. #include <windows.h> 2. #include <gl\gl.h> 3. #include <gl\glu.h> 4. 5. #pragma comment(lib,"opengl32.lib") 6. #pragma comment(lib,"glu32.lib") 7. 8. #define VENTANA_ANCHO 640 9. #define VENTANA_ALTO 480 10. 11. HDC hdc=NULL; 12. HGLRC hglrc=NULL; 13. HWND hWnd=NULL; 14. HINSTANCE hInstance; 15. 16. void inicializarGL(void) 17. { 18. glClearColor(0.0f, 0.0f, 0.0f, 0.5f); 19. glMatrixMode (GL_PROJECTION); 20. glViewport (0, 0, VENTANA_ANCHO , VENTANA_ALTO); 21. gluPerspective(45.0f,VENTANA_ANCHO/VENTANA_ALTO,0.1f,100.0f); 22. gluLookAt( 0,0,16, 0,0,0, 0,1,0); 23. glMatrixMode (GL_MODELVIEW); 24. glLoadIdentity(); 25. } 26. 27. void render(void) 28. { 29. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 30. 31. glBegin(GL_TRIANGLES); 32. glColor3f(0.0f,0.0f,1.0f); 33. glVertex3f(3.0f,-2.0f,0.0f); 34. glVertex3f(0.0f,1.0f,0.0f);

35. 36.
37. 38. 40.

glVertex3f(-3.0f,-2.0f,0.0f); glEnd(); } {

39. void deInicializar(void) 41. 42. 43. 44. 45.


wglMakeCurrent(NULL,NULL); wglDeleteContext(hglrc); ReleaseDC(hWnd,hdc); DestroyWindow(hWnd); UnregisterClass("OpenGL",hInstance); } HWND hWnd,UINT uMsg,WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CLOSE: { PostQuitMessage(0); return 0; } } return DefWindowProc(hWnd,uMsg,wParam,lParam); }

46. 47. 49.

48. LRESULT CALLBACK WndProc( 50. 51. 52. 53. 54. 55. 56. 57. 58.

59. 60. 62. 63.

61. int WINAPI WinMain(HINSTANCE hinst,HINSTANCE hinstAnterior,LPSTR


cadenaDeComandos,int parametroVentana) { MSG mensaje; 64. BOOL continuar=TRUE; 65. GLuint PixelFormat; 66. WNDCLASS Cwnd; 67. 68. hInstance = GetModuleHandle(NULL); 69. Cwnd.style = CS_OWNDC; 70. Cwnd.lpfnWndProc = (WNDPROC) WndProc; 71. Cwnd.cbClsExtra = 0; 72. Cwnd.cbWndExtra = 0; 73. Cwnd.hInstance = hInstance; 74. Cwnd.hIcon = NULL; 75. Cwnd.hCursor = LoadCursor(NULL, IDC_ARROW); 76. Cwnd.hbrBackground = NULL; 77. Cwnd.lpszMenuName = NULL; 78. Cwnd.lpszClassName = "OpenGL"; 79. 80. RegisterClass(&Cwnd); 81. 82. hWnd=CreateWindow( "OpenGL", 83. "Hola Mundo",

84.

85. 88. 89. 91. 93. 94. 95. 96.

86. 87.

90. 92.

WS_OVERLAPPEDWINDOW ,100, 100, VENTANA_ANCHO, VENTANA_ALTO, NULL, NULL, hInstance, NULL);

static PIXELFORMATDESCRIPTOR pfd= { sizeof(PIXELFORMATDESCRIPTOR), 1, 97. PFD_DRAW_TO_WINDOW |PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, 98. PFD_TYPE_RGBA, 99. 32, 100. 0, 0, 0, 0, 0, 0, 101. 0, 102. 0, 103. 0, 104. 0, 0, 0, 0, 105. 32, 106. 0, 107. 0, 108. 0, 109. 0, 110. 0, 0, 0 111. }; 112. 113. hdc=GetDC(hWnd); 114. PixelFormat=ChoosePixelFormat(hdc,&pfd); 115. SetPixelFormat(hdc,PixelFormat,&pfd); 116. hglrc=wglCreateContext(hdc); 117. wglMakeCurrent(hdc,hglrc); 118. 119. ShowWindow(hWnd,SW_SHOW); 120. SetForegroundWindow(hWnd); 121. SetFocus(hWnd); 122. 123. inicializarGL(); 124. 125. while(continuar) 126. { 127. if (PeekMessage(&mensaje,NULL,0,0,PM_REMOVE)) 128. { 129. if (mensaje.message==WM_QUIT) 130. { 131. continuar=FALSE; 132. }

133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143. 144.
145.

else { TranslateMessage(&mensaje); DispatchMessage(&mensaje); } } else { render(); SwapBuffers(hdc); } } deInicializar(); return (mensaje.wParam); }

146. 147. 148.

La aplicacin luce as:

Aun este programa tiene que abarcar bastantes prestaciones no contempladas, por dar simpleza (de ser posible eso) al Hola Mundo. Entre estas prestaciones estn:

Solicitar modo pantalla completa, que es muy comn entre los videojuegos. Atender a los errores en todo el proceso de creacin de la ventana y asociacin a OpenGL. Muchas de las funciones utilizadas en la aplicacin tienen un retorno que indica si la funcin pudo llevarse a cabo o no. Cambiar de tamao la escena de OpenGL al cambiar el tamao de la ventana. Esto se hace aadiendo los estilos CS_VREDRAW y CS_HREDRAW al WNDCLASS e interceptando el mensaje WM_SYSCOMMAND, Wparam SC_SIZE. Pausar la aplicacin cuando la ventana es puesta detrs de otra o minimizada.

Tambin es importante aclarar que este ejemplo utiliza el driver de software de OpenGL creado por Microsoft en la dcada del 90 y que actualmente dicho driver no es mantenido y es muy ineficiente. Para utilizar el driver de OpenGL que proporciona la tarjeta grfica, que tiene la ventaja de ser acelerado por hardware y mantenido constantemente (dependiendo de la tarjeta) se pueden usar las funciones WGL-ARB aprobadas en el ao 2000 y que hacen actualmente parte del estndar, aunque siempre que se quieran usar, se debe comprobar su disponibilidad, ya que dependen de la implementacin del estndar. Finalmente es necesario sealar que esta es una de las maneras mas complicadas de hacer una aplicacin con OpenGL para Windows, ya que se trabaja el API de win32 a muy bajo nivel y las funciones para ligar OpenGL con GDI no han sido actualizada en muchos aos, de hecho

el articulo relevante mas reciente que se puede encontrar en MSDN acerca de OpenGL data del ao 1995.

Tipos de Datos, Convenciones y Estados en OpenGL


Este tutorial hace un breve repaso de tres conceptos importantes en la realizacin de aplicaciones con OpenGL, las convenciones del API, los tipos de datos manejados por este y los estados, con los cuales se controla gran parte del proceso de render. TIPOS DE DATOS Al igual que c, c++ y todos los lenguajes de programacin definen unos tipos de datos bsicos para representar valores de verdad, enteros, flotantes, bits y caracteres, OpenGL define sus propios tipos de datos que permiten trabajar de manera mas uniforme sobre el API en diferentes compiladores y sistemas operativos. Los tipos de datos en OpenGL son los siguientes:
Tipo GL Prefij Tama Descripcin o o 1 8 8 8 16 16 32 32 32 ptrbits Valor de verdad Byte con signo, complemento a 2 Byte sin signo Caracter Entero corto Entero corto sin signo Entero con signo, complemento a 2 Entero sin signo Entero largo no-negativo Entero complemento de 2 , es el numero de bits necesarios para representar un puntero a GLint Entero no-negativo, es el numero de bits necesarios para representar un puntero a GLsizei

GLboolea n GLbyte b

GLubyte ub GLchar -

GLshort s GLushort us GLint GLuint GLsizei i ui -

GLintptr GLsizeipt r

ptrbits

GLbitfield GLfloat f

32 32 64 64

Campo de bits Numero de punto flotante Punto flotante de precisin doble. Doble convertido al intervalo [0,1]

GLdouble d GLclamp d

Las letras de la columna Prefijo se usan en las convenciones de funciones, para identificar el tipo de dato que la funcin recibe. Los bits en la columna de Tamao indican la mnima cantidad que debe tener el tipo de dato, ya que dependiendo de la implementacin del fabricante de hardware este nmero puede ser mayor. Las entrada de las funciones de OpenGL pueden ser cualquier tipo de dato que cumpla las especificaciones de arriba, es decir, el API no solamente funciona con los tipos de datos propios, sino que tambin puede funcionar con los tipos de datos de c, c++, algn binding de java, etc. As es que por ejemplo se puede usar el float de c++ como entrada de una funcin con parmetro GLfloat de OpenGL sin problemas. Aun as es recomendable utilizar los tipos de datos de OpenGL para mantener una buena portabilidad de la aplicacin. CONVENCIONES Todas las funciones de OpenGL siguen unas simples convenciones que permiten identificar su funcionamiento de manera rpida para el programador, dichas convenciones fueron establecidas inicialmente por SGI y hasta hace poco lo eran por el ARB. Actualmente y con el traspaso del ARB al grupo Kronos, se espera que dichas normas sean dictadas desde all. Una diseccin de una funcin en OpenGL lucira algo as como:

Retorno: El retorno puede ser cualquiera de los tipos especificados anteriormente. Liberara: Indica la fuente de la funcin, las mas comunes son gl para OpenGL, glu para el OpenGL Utility Library y wgl para la librera de bindings OpenGL win32. Para el caso de las extensiones, aparecen de la

forma GL_EXT, GL_ARB, GL_NV (nvidia), GL_ATI y en general cualquier nombre de compaa que produzca extensiones de hardware para OpenGL. Nombre: El nombre de la funcin. # Parmetros: Cantidad de parmetros que recibe la funcin, pueden ser ninguno (se obvia), 1, 2, 3 o 4. Tipo parmetros: Indica el tipo de todos los parmetros que recibe la funcin. Pueden ser cualquiera de los tipos con prefijo indicados en la tabla de Tipos de Datos de OpenGL. Si se le agrega la letra v despus del prefijo de tipo de parmetro, indica que la funcin recibe un arreglo de tamao # Parmetros y tipo Tipo parmetros. Parmetros: Los parmetros de la funcin.

Las mismas reglas anteriores pueden ser representada al estilo ANSI C se vera como: (Tomado de la especificacin 2.1 de OpenGL) tipoR Nombre{e1234}{e b s i f d ub us ui}{e v} ( [args ,] T arg1 , . . . , T argN [, args] ); donde tipoR es el tipo de retorno, seguido por el nombre de la funcin (incluido el indicador de librera) , los {} indican que un valor debe ser escogido entre todos los posibles, e es el valor vaci y los argumentos [args,] pueden o no estar presentes. Uno de los ejemplos mas amplios para mostrar el funcionamiento de estas convenciones es de de glVertex(), todas las definiciones de estas funcin son:
PLAIN TEXT
C++:

1. void 2. void 3. void 4. void 5. void 6. void 7. void 8. void 9. void 10. void 11. void 12. void 13. void 14. void 15. void 16. void 17. void 18. void 19. void 20. void

glVertex2d (GLdouble x, GLdouble y); glVertex2dv (const GLdouble *v); glVertex2f (GLfloat x, GLfloat y); glVertex2fv (const GLfloat *v); glVertex2i (GLint x, GLint y); glVertex2iv (const GLint *v); glVertex2s (GLshort x, GLshort y); glVertex2sv (const GLshort *v); glVertex3d (GLdouble x, GLdouble y, GLdouble z); glVertex3dv (const GLdouble *v); glVertex3f (GLfloat x, GLfloat y, GLfloat z); glVertex3fv (const GLfloat *v); glVertex3i (GLint x, GLint y, GLint z); glVertex3iv (const GLint *v); glVertex3s (GLshort x, GLshort y, GLshort z); glVertex3sv (const GLshort *v); glVertex4d (GLdouble x, GLdouble y, GLdouble z, GLdouble w); glVertex4dv (const GLdouble *v); glVertex4f (GLfloat x, GLfloat y, GLfloat z, GLfloat w); glVertex4fv (const GLfloat *v);

21. void 22. void 23. void 24. void

glVertex4i (GLint x, GLint y, GLint z, GLint w); glVertex4iv (const GLint *v); glVertex4s (GLshort x, GLshort y, GLshort z, GLshort w); glVertex4sv (const GLshort *v);

Como se evidencia, es posible adivinar el tipo de entrada que tendr la funcin, solamente con observar su nombre. No todas las funciones poseen numero de argumentos variable, as que en esos casos se omite el numero delante del nombre de la funcin, como por ejemplo en glTranslate(). Esto tambin puede ocurrir con el tipo de datos y si la funcin recibe como parmetros arreglos.

Tipos de Datos, Convenciones y Estados en OpenGL


ESTADOS Como se haba mencionado anteriormente OpenGL mantiene una considerable cantidad de variables de estado. Hay dos tipos diferentes de variables de estado, unas son las llamadas GL server state (estado en servidor GL) y otras las GL client state (estado en cliente GL).

La mayora de los estados que se manipulan a la hora de hacer un programa son del servidor GL. Los estados se pueden prender usando la funcin:

glEnable(GLenum state) que recibe como parmetro el enum correspondiente al estado que se desea activar o desactivar , se puede comprobar si un estado est activo o no mediante: isEnabled(GLenum state) y finalmelmente se puede desactivar el estado mediante: glDisable(GLenum state) Algunos de los estados de comn uso son:

GL_FOG : Estado para la muestra de niebla. GL_LIGHTING: Estado para el uso de luces. GL_ALPHA_TEST: Para hacer uso de las transparencias. GL_DEPTH_TEST: Habilita la prueba de profundidad sobre el buffer de profundidad. GL_LIGHT#: Habilita una luz, que es un nmero entre 0 y 7 (8 es un minimo de luces que debe soportar una implementacin de OpenGL). Para que esto funcione GL_LIGHTING debe estar habilitado. GL_COLOR_MATERIAL: Permite crear materiales de color que afectan el brillo y la difusin de las superficies. Para que esto funcione GL_LIGHTING debe estar habilitado. GL_NORMALIZE: Normaliza los vectores que se pasen a travs de la funcin glNormal(). GL_POLYGON_SMOOTH: Dibuja polgonos con suavizado. Para que esto funcione, se necesita el alpha buffer. GL_POINT_SMOOTH: Suaviza los puntos. GL_CULL_FACE: Si esta habilitado, elimina la parte de atrs de los polgonos (tringulos, rectngulos y polgonos). Se considera frente y atrs de un polgono dependiendo de el orden en que se suministran los puntos que conforman el polgono (sentido reloj o contra-reloj). GL_TEXTURE_2D: Permite el uso de texturas 2d sobre las primitivas.

Bien esto es todo por este tutorial. A pesar de que en este tutorial no se desarrollaron ejemplos prcticos, existes bastantes elementos de aplicacin posterior a la hora de realizar cualquier aplicacin para OpenGL.

Primitivas en OpenGL
Este tutorial cubre el manejo de primitivas en OpenGL, fundamental a la hora de realizar cualquier tipo de aplicacin 3d, ya que con estas (especialmente los tringulos) es que se representan los objetos 3d en los videojuegos. Al igual que en los anteriores tutoriales vale la pena aclarar que la documentacin sobre la que se basa este tutorial es la de la

implementacin 1.1 de OpenGL, que an sigue vigente en la implementacin actual, pero con variaciones gracias a la introduccin de nuevas tecnologas en las tarjetas grficas. Este tutorial, a diferencia de los anteriores, permite ver en accin las capacidades de OpenGL al mismo tiempo que su sencillez, posteriormente, con la introduccin de las transformaciones, texturas, iluminacin, extensiones y shaders se mostrara la potencia que la actual versin de OpenGL permite. PRIMITIVAS En el mundo fsico los objetos poseen formas variadas que no necesariamente guardan relacin con los objetos en la geometra bsica. En los videojuegos y dems software 3d en tiempo real, representar esas formas de manera exacta sera muy costoso computacionalmente hablando (hardware), al punto de que sera casi imposible con la tecnologa actual, es por eso que se usan aproximaciones para hacer que las cosas se vean lo mas cercanas posibles a la realidad y al mismo tiempo, lo mas ajustado posible a las capacidades del hardware actual. Al fin y al cabo hay un dicho en computacin grfica si luce bien, esta bien.

Un modelo hecho de tringulos a la izquierda, a la derecha con superficie e iluminacin. OpenGL hace uso del lo que sus creadores llaman el paradigma Begin/End, que simplemente es la forma en que se pasan las instrucciones para indicar al API como se deben armar las primitivas, a travs de dos funciones: void glBegin( enum mode ); void glEnd( void ); La funcin glBegin() indica a OpenGL que los siguientes llamados a la funcion glVertex(), entre esta y glEnd() debern ser interpretados como vrtices para ensamblar la primitiva especificada en (enum mode), el parmetro de la funcin glBegin(). Adicionalmente a especificar vrtices es posible especificar colores, normales, materiales y texturas entre los llamados de glBegin() y glEnd(). Los vrtices sumistrados entre los llamados del glBegin() glEnd() pueden ser cualquiera de la forma:

void Vertex{234}{sifd}v( T coords ); que significa que pueden tener dos, tres o cuatro parmetros y ser entero corto, entero, flotante o doble, al mismo tiempo que pueden o no estar contenidos en un vector. Es posible especificar diez tipos de primitivas diferentes al llamar la funcin glBegin(), estas son: 1. Puntos - glBegin(GL_POINTS); Se dibuja un punto en las coordenadas especificadas y con el color actual por cada llamado a glVertex. Esta primitiva se ve afectada entre otras por las siguientes funciones: void glPointSize( float size ); : Cambia el tamao de los puntos en pxeles por size, el tamao por defecto es 1.0. El tamao de un punto varia con la distancia de a la cmara por lo que a mayor distancia se vera mas pequeo para dar un efecto de profundidad Esta primitiva se ve afectada entre otros por este estado: GL_POINT_SMOOTH : Indica si el punto debe se suavizado o no. Ejemplo:
PLAIN TEXT
C++:

1. glPointSize(3.0f);

2. 3. glBegin(GL_POINTS); 4. glVertex3f(1.0f,1.0,1.0); 5. glVertex2f(3.5f,1.0f); 6. glEnd(); 7.

Este cdigo dibuja dos puntos, uno en (1,1,1) y (3.5,1,0), de tamao 3 pxeles y con sus respectivas atenuaciones de tamao, dependientes de la distancia a la cmara. A pesar de que el llamado de glBegin() no genera un bloque de cdigo como los corchetes, es recomendable usar un espacio tabulado para indicar que esas lneas de cdigo se encuentran entre el llamado a un glBegin() y un glEnd(). Debido a que hacer un ejemplo mostrando un punto en pantalla es bastante seco y aburrido, la aplicacin que muestra el conjunto de puntos en forma de cilindro, utilizando las funciones seno y coseno para trazar la circunferencia e incrementando una variable de profundidad.

Primitivas en OpenGL
LINEAS OpenGL tiene tres primitivas para pintar lneas, estas son individuales, conectadas y en ciclo. Todas estas formas de dibujar lneas varan en la manera en que se interpretan una serie de vrtices. El dibujo de lneas se ve afectadas por las siguientes funciones: glLineWidth(GLfloat width); : Cambia el tamao de representacin de las lneas por width ( el tamao por defecto es 1), el tamao debe ser mayor que 0. glLineStipple(GLint factor, GLushort pattern) : Genera un patrn segn el cual sern pintadas las lneas. Pattern es un valor de 16 bits, donde cada bit indica si un segmento del tamao de un pxel debe estar apagado o prendido. Factor es una modificacin a la cantidad de pxeles que representa un bit en pattern, as si factor es 5 cada bit en pattern ser dibujado como 5 pxels, bien sean estos prendidos o apagados. En general las 3 primitivas de lneas se ven afectadas por los estados:

GL_LINE_SMOOTH : Indica si las lneas deben ser suavizadas o no. GL_LINE_STIPPLE : Si este estado esta activo se aplica el patrn definido por glLineStipple(). 2. Lneas Individuales glBegin(GL_LINES); Con esta primitiva es posible dibujar lneas separadas. Cada para de vrtices especificados se dibuja una lnea y si se especifica un numero impar de vrtices, el ltimo es ignorado. Ejemplo:
PLAIN TEXT
C++:

1. glEnable(GL_LINE_SMOOTH);

2. 3. glBegin(GL_LINES); 4. glVertex3f(0.0f,0.0f,0.0f); 5. glVertex3f(1.0f,0.0f,0.0f); //eje x 6. glVertex3f(0.0f,0.0f,0.0f); 7. glVertex3f(0.0f,1.0f,0.0f); //eje y 8. glVertex3f(0.0f,0.0f,0.0f); 9. glVertex3f(0.0f,0.0f,1.0f); //eje z 10. glEnd(); 11.

Estas lneas de cdigo dibuja un 3 segmentos de lneas de tamao 1, a lo largo de cada uno de los 3 ejes (x,y,z), partiendo del origen (0,0,0). 3. Lneas Conectadas glBegin(GL_LINE_STRIPS); Permite dibujar un conjunto de lneas conectadas, donde los dos primeros vrtices forman una lnea y cada vrtice adicional genera una lnea que une dicho a vrtice con ltimo suministrado antes de este. Ejemplo:
PLAIN TEXT
C++: 1. 2. glBegin(GL_LINE_STRIPS); 3. glVertex3f(0.0f,0.0f,0.0f); 4. glVertex3f(1.0f,0.0f,0.0f); //Primera linea 5. glVertex3f(1.0f,1.0f,0.0f); //Segunda linea 6. glVertex3f(0.0f,1.0f,0.0f); //Tercera linea

7. glVertex3f(0.0f,0.0f,0.0f); //Cuarta linea 8. glEnd();


9.

Este cdigo dibuja un cuadrado de lado 1 sobre el plano XY. 4. Lneas en Ciclo glBegin(GL_LINE_LOOP); Similar a LINE_STRIPS, con la diferencia de que la ltimo vrtice en ser suministrado se une con el primero para formar una ltima lnea. Ejemplo:
PLAIN TEXT
C++: 1. 2. glBegin(GL_LINE_ LOOP); 3. glVertex3f(0.0f,0.0f,0.0f); 4. glVertex3f(1.0f,0.0f,0.0f); 5. glVertex3f(1.0f,1.0f,0.0f); 6. glVertex3f(0.0f,1.0f,0.0f); 7. glEnd(); 8.

Este ejemplo dibuja lo mismo que el anterior, observando claro est que en este caso no se suministra el punto (0,0,0) al final, ya que esta lnea se genera al conectar el punto (1,1,1) con el primer vrtice.

Primitivas en OpenGL
POLGONOS OpenGL permite el manejo de tres tipos de primitivas poligonales, tringulos, rectngulos y polgonos en general. Las siguientes funciones afectan la manera en que son mostrados los polgonos: void glFrontFace( enum dir ); : Establece el sentido de winding de los polgonos, dir puede ser CCW (Counter-ClockWise), que indica en sentido anti-manecillas o CW (ClockWise) en sentido manecillas. Se puede entender mas fcilmente si se piensa en la primitiva como si esta fuera a ser dibujada en una hoja, con el plano que forman los vrtices, si los puntos de la figura se proveen en sentido anti-manecillas, la cara frontal del polgono estar apuntando hacia fuera de la hoja, de lo contrario (en sentido manecillas) estar apuntando hacia adentro. Es importante tener esto en cuenta debido a que algunas funciones de OpenGL dependen de este comportamiento o lo modifican. void glShadeModel( enum mode ); : Indica de que manera se deben rellenar de color los polgonos, SMOOTH indica que se debe hacer un degradado de color dependiendo del color de cada vrtice y FLAT que no se realizar degradado. void CullFace( enum mode ); : Indica cual de las caras del polgono no debe ser dibujada en caso de que el estado CULL_FACE est activo, las opciones son FRONT, BACK y FRONT_AND_BACK, para las caras frontal, posterior y frontal-posterior simultneamente. void PolygonMode( enum face, enum mode ); : Modifica la forma en que son dibujados los polgonos, face indica la cara a la que se le cambiaran las propiedades, al igual que CullFace las opciones son FRONT, BACK Y FRONT_AND_BACK, mode indica como se desea presentar el polgono, las opciones son POINT, que muestra solo los vrtices, LINE, que muestra las lneas que lo conforman y FILL que muestra la superficie del mismo. El modo por defecto es FRONT_AND_BACK y FILL. Los siguientes estados estn asociados al manejo de las primitivas poligonales: CULL_FACE : Indica si sobre los polgonos se debe realizar la eliminacin de caras, por defecto est deshabilitada. GL_DEPTH_TEST : Indica si se debe realizar un chequeo de profundidad para eliminar los segmentos de objetos ocultos tras otros objetos. Es sin duda esencial realizar este chequeo para dar sentido de profundidad a la escena. Este estado esta por defecto desactivado. TRIANGULOS

Sin duda esta es una de las primitivas mas utilizadas, debido a que posee ciertas ventajas sobre los rectngulos y polgonos, por ejemplo, no es posible suministrar tres vrtices que no pertenezcan a un mismo plano, mientras que esto si es posible con las otras dos primitivas. Existen tres tipos posibles de primitivas triangulo en OpenGL, independientes, conectados y en abanico. 5. Tringulos independientes glBegin(GL_TRIANGLES); Simple, cada tres vrtices se genera un nuevo triangulo con la cara frontal determinada por el orden que este activo, bien sea CCW o CW. Evidentemente la cantidad de vrtices debe ser 0 o mltiplo de tres, los ltimos vrtices que no correspondan a ninguna tripleta son despreciados. Ejemplo
PLAIN TEXT
C++: 1. 2. glBegin(GL_TRIANGLES); 3. glVertex3f(0.0f,0.0f,0.0f); 4. glVertex3f(1.0f,0.0f,0.0f); 5. glVertex3f(0.0f,1.0f,0.0f); 6. glEnd(); 7.

Este ejemplo genera un triangulo rectngulo sobre el plano XY con la cara frontal apuntando hacia Z+ en CCW. 6. Tringulos conectados glBegin(GL_TRIANGLE_STRIP); Similar a LINE_STRIPS, los primeros tres vrtices generan un triangulo y luego cada vrtice adicional genera otro que comparte un lado del triangulo anterior, es decir si se suministran los vrtices a,b,c,d,e se formarn los tringulos abc , cbd y cde. La razn por la que se invierte el orden de los vrtices en algunos tringulos, cbd por ejemplo, es para preservar el winding de la primitiva. Ejemplo:
PLAIN TEXT
C++: 1. 2. glBegin(GL_TRIAGLE_STRIP); 3. glVertex3f(0.0f,0.0f,0.0f);

4. glVertex3f(1.0f,0.0f,0.0f); 5. glVertex3f(0.0f,1.0f,0.0f); 6. glVertex3f(1.0f,1.0f,0.0f); 7. glEnd();


8.

Este fragmento de cdigo dibuja un cuadrado de lado 1 sobre el plano XY, hecho de dos tringulos y con la cara frontal apuntando hacia Z+. 7. Tringulos en abanico glBegin(GL_TRIANGLE_FAN); Esta primitiva genera un conjunto de tringulos alrededor de un punto comn, al igual que TRIANGLE_STRIPS, estos estn conectados por un lado. En este caso el proporcionar los vrtices a,b,c,d,e generarn los tringulos abc, acd y ade. Ejemplo:
PLAIN TEXT
C++: 1. 2. #define PI 3.1415926535897932384626433832795f 3. ... 4. int n= 2*PI/50; 5. glBegin(GL_TRIANGLE_FAN); 6. glVertex3f(0.0f,0.0f,0.0f); 7. for(int seg=0, seg<50;seg++) 8. { 9. glVertex3f(cos(seg*n), sin(seg*n),0.0f); 10. } 11. glEnd(); 12.

Este cdigo dibuja un crculo de radio 1 hecho de n-1 segmentos de tringulo, sobre el plano XY y con la cara frontal hacia Z- en sentido CW.

Primitivas en OpenGL
RECTNGULOS A diferencia de los tringulos, con esta primitiva hay que tener bastante cuidado ya que es posible suministrar cuatro vrtices que no sean coplanares (que compartan el mismo plano), en cuyo caso OpenGL puede dibujarlo de manera incorrecta. Hay nicamente dos tipos de primitiva rectngulo en OpenGL, los independientes y conectados. 8. Rectngulos Independientes glBegin(GL_QUADS); Cada cuatro vrtices proporcionados es generado un rectngulo, si son proporcionados vrtices que no corresponden a ningn grupo de cuatro, dichos vrtices son descartados. Ejemplo:
PLAIN TEXT
C++: 1. ... 2. glBegin(GL_QUADS); 3. glVertex3f(0.0f,0.0f,0.0f); 4. glVertex3f(1.0f,0.0f,0.0f); 5. glVertex3f(1.0f,1.0f,0.0f); 6. glVertex3f(0.0f,1.0f,0.0f); 7. glEnd(); 8.

Este segmento de cdigo dibuja un cuadrado de lado 1 sobre el plano XY, con la cara frontal en sentido Z+ en CCW. 9. Rectngulos Conectados glBegin(GL_QUAD_STRIP); Los primeros cuatro vrtices generan un rectngulo, luego cada par adicional de vrtices genera un nuevo rectngulo conectado con los dos ltimos vrtices especificados en el anterior. Esta primitiva es bastante particular, ya que los bordes deben ser suministrados de manera alternada con el fin de preservar el winding, esto se ve mas claramente en el ejemplo y el grfico. Ejemplo:
PLAIN TEXT
C++: 1. 2. glBegin(GL_QUAD_STRIP); 3. glVertex3f(0.0f,0.0f,0.0f); 4. glVertex3f(0.0f,1.0f,0.0f); 5. glVertex3f(1.0f,0.0f,0.0f); 6. glVertex3f(1.0f,1.0f,0.0f); 7. glVertex3f(0.0f,1.0f,1.0f); 8. glVertex3f(1.0f,1.0f,1.0f); 9. glEnd(); 10.

Este cdigo dibuja dos cuadrados de lado 1, el primero en el plano XY con la cara frontal hacia Z+ en sentido CW y el segundo en el plano ZY con la cara frontal hacia X+ en sentido CW. 10. Polgonos Generales glBegin(GL_POLYGON); Esta primitiva es una representacin general de un polgono, lo que incluye un tringulo, rectngulo o cualquier polgono de n lados. Existe una restriccin adicional y es que al especificar polgonos, adems de que todos los vrtices tienen que ser coplanares y es que el polgono tiene que ser convexo. Si alguna o ambas de estas condiciones no se cumplen, OpenGL dibujar los polgonos de formas no esperadas y generalmente indeseables. Para forma un polgono basta suministrar tres o ms vrtices que cumplan las anteriores condiciones. Ejemplo:
PLAIN TEXT
C++:

1. #define PI 3.1415926535897932384626433832795f 2. #define SUBD 13 3. ... 4. int n= 2*PI/SUBD; 5. glBegin(GL_POLYGON); 6. glVertex3f(0.0f,0.0f,0.0f); 7. for(int seg=0, seg<SUBD;seg++) 8. { 9. glVertex3f(cos(seg*n), sin(seg*n),0.0f); 10. } 11. glEnd(); 12.

Este ejemplo es idntico en estructura al de TRIANGLE_STRIP con la diferencia que este formar un solo polgono, en este caso uno regular de n-1 lados.

NOTAS FINALES Bien, eso es casi todo en el tema de primitivas, an as es bueno tener en cuenta algunas cosas sueltas relacionadas con el asunto. No es posible llamar cualquier comando entre un glBegin() y un glEnd(), por ejemplo glIndexPointer(), glNormalPointer(), glTexCoordPointer(), glSecondaryColorPointer() , glVertexPointer(), glVertexAttribPointer(), entre otras no pueden ser llamadas, ya que causaran un error o un estado indefinido. Para una completa referencia ver las especificaciones de OpenGL. Es recomendable utilizar la menor cantidad posible de llamados a glBegin()/glEnd() con el fin de ganar un poco de rendimiento a la hora de ejecutar el programa.

Al momento de utilizar textura e iluminacin ser necesario especificar nuevos parmetros dentro del glBegin()/glEnd(), como son la normal a la superficie y las coordenadas de textura. Si glShadeModel() esta configurado en SMOOTH es posible agregar colores individuales a cada vrtice mediante glColor, que luego sern degradados en la superficie de la primitiva (incluidas lneas). El ejemplo final es un horrendo pajarraco realizado a partir de primitivas, 100% hardcoded.

Bases de Animacin en OpenGL


Este tutorial muestra las bases necesarias para crear animaciones simples en OpenGL, incluyendo los conceptos de doble buffer, la coordinacin de frames por tiempo y pasos. Al igual que en los anteriores tutoriales se dar un ejemplo aplicativo, aunque en este caso est hecho en Visual C++ Express. La Animacin.

Cualquier forma de animacin, bien sea esta al estilo antiguo de Disney (frames hechos a mano), cine, animaciones gif, videos de computadora y videojuegos utiliza una tcnica que consiste en mostrar rpidamente, a entre 30 y 60 cuadros por segundo (fps) un conjunto de imgenes que varan de forma ligera, para crear la sensacin de movimiento aprovechando una caracterstica de la visin humana llamada persistencia de la visin. En el caso particular de los videojuegos, los frames se hacen mezclando un conjunto de figuras bidimensionales (sprites) o haciendo complejos clculos sobre la manera en que se vera una proyeccin bidimensional de una escena abstracta tridimensional (3d), o una mezcla de ambas, y realizando en cada caso un clculo del pequeo movimiento que debe hacer cada objeto para dar una sensacin fluida de animacin. EL BUFFER DOBLE Se ha mencionado ya que para poder dar la sensacin de movimiento es necesario mostrar cerca de 30 cuadros por segundo. El problema es que el computador no genera instantneamente la imagen a partir de los sprites o la escena 3d, por lo que si se posee una sola rea de dibujo en la memoria se vern molestos intervalos entre un frame y otro (mientras se hacen los clculos y se realizan movimientos de memoria). Es en este sentido que la estrategia de mltiples buffers, dos en este caso funciona.

La estrategia es bastante simple, se tienen 2 reas de memoria, una es la que actualmente se muestra en pantalla (Front Buffer) y la otra es sobre la cual se realizan los clculos (back buffer). Cuando la escena se termina de dibujar se intercambian los papeles entre los buffers, es decir, el Back se convierte en Front y visceversa. De esta manera el usuario no ve cuando

se est renderizando sobre el buffer, solo ve el resultado final, con la ventaja adicional de que el proceso es bastante eficiente, ya que no se realizan copias entre reas de memoria, al menos para mostrar los frames. La parte terica funciona de manera similar en diferentes entornos, bien sea DirectX, OpenGL o SDL. En el caso prctico de OpenGL, la especificacin del API no incluye una garanta de que los dos buffers estn presentes (obviamente al menos el Front debe existir), por lo que las funciones para solicitar y manejar mas de un buffer dependen del entorno en el que se est ejecutando la aplicacin (Windows, Mac, Linux, etc). Microsoft provee una implementacin propia del Doble Buffer para Windows y esta misma funcionalidad es ofrecida por Glut y similares para diferentes sistemas operativos de manera genrica. Implementacin Glut Es bastante sencilla (ver tutorial Hola Mundo 3d en OpenGL), basta solicitar el modo Doble Buffer al inicializar la ventana y cada vez que se terminan de pasar los parmetros de dibujo a OpenGL y estos han sido ejecutados, pedir un cambio de Buffer as:
PLAIN TEXT
C++:

1. inicializarVentana()
2. { 3.

4.

5. 6. } 7. 8. render() 9. { 10. //comandos de dibujo 11. glutSwapBuffers(); 12. }

glutInitDisplayMode(GLUT_DOUBLE| )

La funcin glutInitDisplayMode() se encarga de solicitar al sistema el modo requerido por el programador, en este caso Doble Buffer (GLUT_DOUBLE) y otros que se le suministren seguidos de | (GLUT_DOUBLE | GLUT_RGB por ejemplo). glutSwapBuffers() intercambia el Buffer actual sobre el que se est dibujando (Back) con el que se muestra en pantalla (Front). Implementacin Windows La implementacin del doble buffer es algo mas compleja (ver tutorial Hola Complicado Mundo OpenGL Win32), pero bsicamente est ligada al PIXELFORMATDESCRIPTOR HDC con los cuales se solicita y al HDC, con el cual se intercambian los Buffers. El cdigo luce as:

PLAIN TEXT
C++:

1. HDC hdc=NULL;

2. 3. crearVentana() 4. { 5. //todo el cdigo para crear la ventana 6. static PIXELFORMATDESCRIPTOR pfd= 7. { 8. ... 9. ,... |PFD_DOUBLEBUFFER | //tercer parmetro 10. ,... 11. }; 12. PixelFormat=ChoosePixelFormat(hdc,&pfd); 13. } 14. render() 15. { 16. //El cdigo de render 17. SwapBuffers(hdc); 18. }

Donde el pdf es la variable para inicializar el modo de pantalla y hdc es el hardware device context asociado a la ventana.

Bases de Animacin en OpenGL


COORDIANACIN DE FRAMES Debido a que el objetivo de los videojuegos es que puedan ser jugados en la mayor cantidad posible de PC, sin importar que configuracin de hardware posean (lo cual no es totalmente cierto como ser ver mas adelante), es importante que las animaciones generadas por ste se vean bien en esta gama de equipos, es decir, que la animacin no se vea cortada porque no se pueden generar a suficiente velocidad los frames (PC anticuado) o acelerada, porque los clculos se realizan mas velozmente de planeado lo por los creadores del juego (PC con hardware 1a) . Es en este sentido que son necesarias tcnicas que de alguna manera resuelvan estas dos ocurrencias, de manera que el jugador pueda disfrutar del videojuego sin importar que PC tenga. Limitando los FPS Cuando los frames se producen en tiempos menores a los deseados (menos de 1/30, 1/60, etc), basta demorar un poco la aparicin de cada frame para que la animacin luzca igual en equipos diferentes. Para hacer esto se puede medir cuanto tiempo ha pasado desde que se mostr el ltimo frame y, estando ya hecho el frame a montar, esperar el tiempo que haga falta para que aparezca en el tiempo correcto, es decir, 1/30 de segundo despus de haber mostrado el ltimo.

Para mostrar el frame se hace referencia a hacer el cambio entre buffers. El cdigo utilizando los ticks de Windows para realizar el conteo de tiempo se vera as:
PLAIN TEXT
C++:

1. DWORD tiempo = 0;
2.

3. render()
4. {

5. 6. 7. 8.

//El cdigo de render de la escena. while( GetTickCount() tiempo <30) { //Hacer ac cualquier cosa no muy costosa en tiempo computacional 9. } 10. SwapBuffers(hdc); //o cualquier otra funcin de cambio de buffers 11. 12. tiempo = GetTickCount(); 13. }

GetTickCount() devuelve el numero de milisegundos que han pasado desde que el sistema inicio con una precisin es 10 ms (en sistemas anteriores como 95,98 y ME el tick tendr una resolucin de 55ms) , por lo que el programa correr a entre 30 y 34 fps. La razn de reiniciar el conteo despus de que se termina de dibujar el frame y no al principio de la funcin render() es que el tiempo de generacin del frame no solo va a corresponder a las operaciones de dibujo, sino tambin puede ser las de colisiones, IA, interpretacin de scripts, etc, por lo

que estos tiempos tambin deben ser tenidos en cuenta a la hora de realizar el clculo del 1/30 de segundo. Las instrucciones que van en el ciclo de espera no necesariamente tienen que ser para perder tiempo, pueden hacerse clculos, recibir la entrada del usuario, inspeccionar los eventos del sistema, o cualquier otra cosa que no ocupe una fraccin significativa de 1/30 de segundo. Coordinacin por movimiento. Este tipo de coordinacin es posible usarla tanto para ms frames como para menos. Esta basada en predecir en que punto de su recorrido se encontrara un objeto que sigue su trayectoria en un tiempo particular bien sea regulando todos los movimientos con funciones de tiempo o interpolando los valores de puntos conocidos. El dibujo de los objetos se hace basado en el tiempo que se demor el PC en realizar los clculos, por lo que en general la medida del tiempo debera ser ms exacta. Para esto se puede utilizar el contador de alta resolucin de Windows, disponible a travs del plataform SDK.

En esta grfica se muestra una bola que rebota, de la cual se espera que produzca un frame cada vez que t aumente en 1, aunque en realidad los frames se estn produciendo en los tiempos t= { 0, 0.5 , 1, 1.2, 1.8, 2 }. Debido a que la bola se rige por una ecuacin paramtrica de tiempo (ehhh, clculo vectorial?), es posible predecir en que punto se encontrar en cada uno de dichos tiempos, por lo que los cuadros intermedios se pueden renderizar conservando el movimiento como se debera observar si solo se renderizara t = {0, 1 , 2}. Un pseudocdigo que mostrara la coordinacin por movimiento (con un contador de Ticks que no es el ideal) :
PLAIN TEXT

C++:

1. struct obj
2. { 3. Vector3d pos; 4. Vector3d vel; 5. }Obj; 6. 7. Dword tiempoAct = GetTickCount(), tiempoPas, deltaT; 8. 9. 10. render() 11. { 12. // 13. tiempoPas = tiempoAct; 14. tiempoAct = GetTickCount(); 15. deltaT = tiempoAct tiempoPas; 16. 17. //Se actualiza la posicin de todos los objetos 18. for( int i=0; i<NUMOBJ; i++) 19. { 20. losObj[i]->pos = vel*deltaT; 21. //dibujar el objeto i de la coleccion losObj[] 22. } 23. }

Hay que notar entonces que los objetos haran en este ejemplo saltos proporcionales al tiempo transcurrido entre un frame y otro, por lo que siempre se dibujaran en pantalla en la posicin adecuada, sin importar si el deltaT fue mayor o menor al recomendado (1/30s , 1/60s, etc). Aunque este tipo de coordinacin parece ideal para cualquier tipo de caso (hardware lento o rpido), hay que tener en cuenta que si el hardware es demasiado pobre en prestaciones se pueden obtener frames por debajo del nivel recomendado, lo que significara una ralentizacin molesta que sera poco deseable a la hora de jugar. Tambin es necesario considerar que si se producen demasiados frames (150?), probablemente la pantalla descartar algunos para adecuarse a la sincronizacin vertical, adems por mucho que se insiste en el tema de la fluidez, se sabe que muy pocos humanos, an en condiciones ideales, pueden percibir diferencias notables por encima de los 75fps. Anotaciones Finales Es importante definir el tipo de coordinacin que se debe utilizar antes de codificar o crear animaciones, debido a que posteriormente un cambio podra ser engorroso de implementar. Hay que tener en cuenta que la coordinacin por frames es fcil de implementar en el sentido en que solo hay que agregar unas cuantas lneas de cdigo a la funcin de render, mientras que la coordinacin por movimiento requiere cambios sobre cada funcin que se encarga de realizar cambios sobre la posicin de los objetos y en los casos en que dichos datos no

pueden ser funciones paramtricas de tiempo (datos de bones, scripts de movimientos, etc), ser necesario realizar una interpolacin numrica sobre los datos conocidos.

Transformaciones en OpenGL
En algunos tutoriales anteriores de OpenGL se han mencionado de manera fragmentaria los conceptos de transformaciones:

En Primeros Pasos en OpenGL se mencionaba de manera general algunos conceptos relacionados con las transformaciones en OpenGL, especficamente la proyeccin. Es recomendable revisar y entender Primeros Pasos en OpenGL antes de aventurarse a leer el presente tutorial. En los Hola Mundo 3d en OpenGL y Hola Complicado Mundo OpenGLwin32 se utilizaron algunas transformaciones de proyeccin y viewport para ajustar la inicializacin de la ventana, pero sin entrar mucho en detalle acerca de su funcionamiento.

Este tutorial explora los diferentes tipos de transformaciones en OpenGL, a saber:


Proyeccin: Trasforman una escena 3d abstracta, en una imagen plana que puede ser visualizada en una pantalla. Viewport: Ajustan el producto de la proyeccin a las dimensiones de un rectngulo contenedor (ventana). De vista: Que definen y afectan la posicin desde la cual se visualizan las escenas tridimensionales. Modelado: Sirven para manipular los objetos en la escena, permitiendo trasladarlos, rotarlos y deformarlos (escalarlos). Modelo-Vista: Son la combinacin de las dos transformaciones anteriores, que desde un punto de vista prctico son semejantes.

Antes de empezar a detallar cada tipo de transformacin es bueno tener una idea de las funciones que permiten acceder directamente a las matrices que representan los estados de las transformaciones:

void glMatrixMode( enum mode ); Permite seleccionar la matriz sobre la cual se realizaran las operaciones, los posibles valores de mode son TEXTURE, MODELVIEW, COLOR o PROJECTION . Por ahora las ms interesantes son MODELVIEW y PROJECTION, las otras se vern en su momento. Void glLoadMatrix{fd} (T m[16]); Recibe una matriz de 44 que reemplaza la actual seleccionada. El arreglo es ordenado en forma de una matriz que tiene orden Y, a diferencia de las matrices convencionales que tienen orden X, lo que quiere decir que tiene la forma:

m = [a1 ,a2 ,a3 ,a4 ,a5 ,a6 ,a7 ,a8 ,a9 ,a10, a11, a12, a13, a14, a15, a16] es interpretada internamente por OpenGL como:

Ambas opciones pueden ser utilizada (m[16] o m[4][4]) , aunque la primera es mas eficiente, debido a que solo genera un puntero de acceso.

void glMultMatrix{fd}( T m[16] ); Multiplica la matriz actual por la matriz m[16] y reemplaza la matriz actual con el resultado de la operacin. La operacin resultante sera algo as como A = A M , donde A es la matriz actual, M es la matriz suministrada y A es la nueva matriz que resulta de la operacin y que reemplaza a A. void glLoadTransposeMatrix{fd}( T m[16] ); Realiza una funcin similar a LoadMatrix(), con la diferencia que trabaja sobre una matriz en orden X as:

que es evidentemente la transpuesta de la m que recibe LoadMatrix. void glMultTransposeMatrix{fd}( T m[16] ); Misma funcionalidad que MultMatrix() , solo que acta en base al la matriz en orden X, o sea la transpuesta. void glLoadIdentity( void ); Remplaza la matriz actual por la matriz identidad de tamao 44.

Estas operaciones afectan directamente las matrices mencionadas anteriormente, debido a que las operaciones de alto nivel (trasladar, rotar, escalar) que existen mas adelante se concatenan, es decir su efecto se acumula sobre matriz actual, existen dos operaciones que permiten guardar la matriz actual en una pila y restaurarla cuando sea necesario, estas son:

void glPushMatrix( void ); Coloca una copia de la matriz actual en la parte superior de la pila correspondiente. void glPopMatrix( void ); Saca el elemento superior de la pila, que pasa a reemplazar a la matriz actual.

Estas dos operaciones son muy utilizadas, debido a que permiten realizar transformaciones sobre objetos diferentes, manteniendo algunas de las anteriores sin modificar. Hay una ltima funcin que es til en la manipulacin de matrices y otros aspectos de OpenGL:

void glGetFloatv(enum value, float *data); Permite obtener una copia de aquello que se indica en value. Por ejemplo si se pasan como parmetros MODELVIEW_MATRIX y un apuntador a un arreglo de flotantes tamao 16 se obtiene una copia de dicha matriz a travs del arreglo.

Transformaciones en OpenGL
Proyeccin Como ya se ha visto en tutoriales anteriores, OpenGL maneja 2 tipos de proyeccin, en perspectiva y ortogrfica, donde la primera corresponde a la visin realista de la escena,

mientras que la segunda es una plana que no deforma las dimensiones de los objetos dependiendo de su distancia a la cmara. Ortogrfica: Para ajustar la proyeccin ortogrfica se utiliza el siguiente grupo de funciones: glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far); Esta funcin permite controlar los parmetros del volumen de vista izquierdo, derecho, abajo, arriba, cerca y lejos. Hay que recordar que debido a que no se posee percepcin de profundidad en este modo el valor del volumen deber corresponder en la mayora de los casos a un volumen suficientemente grande para contener los objetos de la escena. gluOrtho2D(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top); Esta funcin es simplemente una forma de la anterior, en donde se ha despreciado el valor de Z asignando los valores near = -1 y far = 1, generalmente se utiliza para escenas planas, en las que los objetos carecen de profundidad. Perspectiva: Existen dos manera de manejar la proyeccin en perspectiva, a travs de de una funcin gl o mediante la librera glu (una tercera puede ser realizar los clculos de la matriz manualmente. En el primer caso: glFrustrum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far) Esta funcin crea una matriz y la multiplica por la matriz de proyeccin actual, la matriz tiene la forma:

Estos no son datos fcilmente digeribles por muchos, as que glu ofrece una versin alternativa, mucho ms fcil de entender: void gluPerspective(GLdouble fov, GLdouble aspect, GLdouble near, GLdouble far); Donde fov se refiere al Field of View: el ngulo medido en la direccin Y dado en grados que es visible al usuario, el aspect que es una razn entre el alto y el ancho de la ventana y los otros dos valores corresponden a la distancia con respecto al plano mas cercano y mas lejano de proyeccin, tomando la cmara (0,0,0) como el punto de partida. Los valores usuales para estas variables de inicializacin son fov = 45, aspect= ancho_ventana/alto_ventana, near = 1.0 y far=100.0 o el valor que sea para estos dos ltimos, segn las necesidades (ambos tienen que ser positivos). Bueno, las modificaciones a las matrices de perspectiva por parte de estas funciones son de realizarse por lo comn una vez durante la creacin de una aplicacin, mientras que las siguientes funciones se utilizan rutinariamente en la creacin de grficos. Transformaciones ModelView Una tarea muy comn en la creacin de grficos 2d, 3d y videojuegos es la de mover objetos par crear cierta animacin. La primera idea que se nos viene a la cabeza en el caso de OpeGL es que todo modelo est formado por primitivas, toda primitiva por puntos y finalmente todo punto por una tripleta de coordenadas XYZ, as que si se cambian las coordenadas todo de ah hacia arriba se mueve.

Despus de un tiempo tal vez se requieran realizar unas tareas un poco mas complicadas como rotar el objeto alrededor de su centro, o alrededor de la posicin donde se encuentra con respecto al sistema coordenada. Esta tarea no es tan simple y requiere algo de conocimiento en matemtico para hacerse, debido a que probablemente implique la implementacin de un sistema para almacenar y operar matrices, implementacin de coordenadas homogneas, etc. OpenGL provee de estos sistemas y utiliza algunas simples funciones para su acceso, contando con tres operaciones bsicas: mover, rotar y escalar un conjunto de vrtices. void glRotate[fd](GLfloat angle, GLfloat x, GLfloat y, GLfloat z); Realiza una rotacin del espacio coordenado por una medida de (angle) tomados en grados a lo largo del vector determinado por (x,y,z). Es recomendable que (x,y,z) representen un vector normalizado (o sea magnitud(x,y,z) = 1), debido a que si no es as OpenGL intentar normalizarlo. La rotacin se lleva siguiendo la regla de la mano derecha, teniendo en cuenta que el vector (x,y,z) apunta con el pulgar hacia adentro (hacia uno). void glTranslate[fd](GLfloat x, GLfloat y, GLfloat z); Esta funcin traslada el espacio coordenado en x, y, z unidades sobre sus respectivos ejes coordenados X, Y, Z. void glScalef(GLfloat x, GLfloat y, GLfloat z); Cambia la proporcin de los objetos respecto a los ejes coordenados, lo que es equivalente a decir que los estira o encoge una cantidad determinada por los parmetros de la funcin x,y,z. Hay que tener en cuenta las siguientes caractersticas a la hora de utilizar estas funciones:

Estas tres operaciones afectan la matriz actual seleccionada, bien sea MODELVIEW o PROJECTION, generalmente para propsitos de rotar objetos dentro de la escena se utiliza la matriz MODELVIEW. El eje coordenado de referencia para las operaciones tanto en MODELVIEW como en PROJECTION se denomina eye coordinates, que traduce coordenadas de ojo o mejor vista. Este es un sistema inamovible y en ausencia de cualquier transformacin, la cmara est situada en (0,0,0) apuntando hacia la direccin Z negativa, con el vector arriba en el sentido Y positivo. Cada operacin que afecta la matriz MODELVIEW crea otro sistema coordenado para los objetos que se dibujen despus de realizada la trasformacin, dicho sistema difiere del bsico, es decir de las llamadas coordenadas de vista (eye coordinates) dependiendo de todo el conjunto de transformaciones realizadas desde el momento en que la matriz MODELVIEW dejo de ser la identidad. Una consecuencia de lo anterior es que las operaciones no son conmutativas, es decir que un llamado de glRotatef(), seguido de uno de glTranslatef() produce un efecto diferente a llamar las operaciones en orden inverso. Esto se ve mejor ilustrado en el grfico:

Es importante aprender a utilizar correctamente glPushMatrix(), glPopMatrix() y otras operaciones que permitan salvar y restaurar estados de matriz, debido a que permiten realizar translaciones y rotaciones especificas para un cierto objeto, sin alterar los otros.

Transformaciones en OpenGL
Para este caso tutorial he creado dos ejemplos utilizando glut y Visual Studio Express:

En el primero se ilustra el uso de la translacin y de glPushMatrix() y glPopMatrix(), utilizando las teclas w,'a,s,'d se pueden mover el tringulo seleccionado y con e se cambia de triangulo a mover. En el segundo se utiliza w,'a,s,'d para girar el un cubo alrededor de los ejes X y Y transformado, mostrando al mismo tiempo las coordenadas de objeto y de ojo.

Superfices e Iluminacion en OpenGL


Hasta ahora la forma de dar color a las primitivas en OpenGL era mediante el llamado a glColor3f() con la que se puede lograr que cada vrtice tenga un color y OpenGL se encarga de difuminar este a travs de la primitiva que se est construyendo. Este tutorial muestra el uso de la iluminacin clsica en OpenGL (fixed function pipeline) y de la asignacin de materiales (color materials) a las superficies. An no se tocar el tema de texturas ni de per-pixel lighting que espero revisar en tutoriales posteriores. Como referente es buena idea revisar algn libro o artculo donde se trate la teora del color, nada muy complicado, con el fin de entender como opera el modelo de color e iluminacin en OpenGL y en general las tcnicas de CG. La iluminacin en OpenGL est dada por dos componentes, la superficie del objeto, que es asignada a las primitivas y un conjunto de luces globales que interactan con las superficies para dar el aspecto final a los objetos. Tanto en las luces como en la superficie es necesario especificar 3 parmetros: ambiental, difuso y brillo. En el caso de la luz, los parmetros se refieren a la emisin y en el caso de la superficie a la absorcin.

COLOR El color es parte de cada componente, tanto de luz como de superficie. Tanto en la realidad como en OpenGL las fuentes de luz pueden tener todas las componentes de colores (luz blanca), solo algunas (luz de colores) o ninguna, que no tiene ningn paralelo real. En el caso de OpenGL estas componentes son especificadas en la tripleta RGB de siempre. Para el caso de la superficie se especifica el color que la superficie mostrara en presencia de una luz blanca, aunque en el caso de OpenGL el color de cada componente de superficies es multiplicado por el de la luz para obtener el color resultante. En el caso de OpenGL el color es una propiedad que se especifica vrtice por vrtice mediante el llamado de glColor() , el color correspondiente a una textura o el producto de un procesado tipo pex-pixel sobre la superficie de un modelo. Este segmento de cdigo, insertado en la funcin de render, por ejemplo:

PLAIN TEXT
C++:

1. glBegin(GL_POLYGON); 2. glColor3f(1.0f,1.0f,1.0f); 3. glVertex3f(-10,15,0); 4. glColor3f(0.0f,0.0f,0.0f); 5. glVertex3f(10,5,0); 6. glColor3f(1.0f,0.0f,0.0f); 7. glVertex3f(10,-5,0); 8. glColor3f(0.0f,1.0f,0.0f); 9. glVertex3f(0,-10,0); 10. glColor3f(0.0f,0.0f,1.0f);
11.

12. glEnd();

glVertex3f

Producir una imagen como esta:

Los colores se expresan en OpenGL mediante una combinacin aditiva de rojo (R, Red), verde (G, Green) y azul (B, Blue). El mnimo en los tres valores indica el color negro y el blanco es expresado por los tres colores en su mxima intensidad. Los colores bsicos se obtienen con ese color a mxima intensidad y los dems en mnima, mientras que la escala de grises se obtiene con iguales intensidades en los tres colores. Es posible especificar un cuarto parmetro denominado alfa, que corresponde a la cantidad de transparencia de ese vrtice en particular. Todas las variaciones de la funcin glColor() se pueden establecer como: void glColor[3,4][x](red, green, blue, alpha); Donde x puede ser cualquiera de los tipos de datos de OpenGL, los cuales se haban visto en un tutorial anterior. Las dos maneras ms comunes de expresar un color en OpenGL son la versin escalada del color y la versin clsica que generalmente es utilizada en otros API grficos como GDI. En la primera versin, los colores son expresado con un numero con coma decimal que se encuentra entre 0 y 1, donde 0 implica la menor intensidad de ese color en particular y 1 la mxima. Para el segundo caso se usa un nmero entero que se encuentra entre 0 y 255. As, estos dos llamados de funciones seran equivalentes y producirn el mismo resultado: glColor3f(0.0f,1.0f,0.5f); glColor3ub((GLubyte)0, (GLubyte)255, (GLubyte)127);

Superfices e Iluminacion en OpenGL


Componente Ambiental (Ambient) La luz ambiental es comnmente asociada a la rayos que inciden sobre el objeto de desde todas direcciones, por lo que no generan ningn tipo de sombreado sin importar la forma del objeto y su posicin con respecto a la cmara. En la realidad este tipo de luz se origina cuando la luz rebota sucesivamente en diferentes superficies hasta que llega al objeto observado, haciendo parecer a este simplemente mas claro, de manera homognea. Es importante ver que excepto en muy raras ocasiones, no es posible observar esta componente por separado, sino sin que sin importar si la luz proviene del sol o de una fuente artificial, hay un poco de cada componente en todos los objetos observados en la realidad. Componente Difuso (Diffuse) En este caso los rayos provienen de una fuente puntual (la fuente es identificable), pero al caer sobre el objeto rebotan dispersndose en diferentes ngulos, por lo que el objeto aparecer mas iluminado en unas zonas que en otras. La forma en que el objeto es iluminado depender entonces de su forma y de la relacin de posiciones cmara-objeto-fuenteDeLuz. Componente Brillo (Specular) Algunas fuentes de luz tienden a producir rayos compactos y paralelos, como por ejemplo el sol y los arreglos en rejilla que se colocan en los fluorescentes de oficinas. Este tipo de luces cuando inciden sobre objetos brillantes como metales pulidos, vidrio, pisos encerados, automviles limpios y encerados, etc, producen zonas donde la luz se refleja mas intensamente, debido a que los rayos rebotan casi paralelos. Esta configuracin es altamente dependiente de la ubicacin de la fuente de luz, el objeto, el observador y de la forma del objeto. Los objetos denominados mate son aquellos que poseen muy poco componente de brillo, debido a las caractersticas del material.

El core de la especificacin clsica de OpenGL (anterior a glsl) no soporta tcnicas avanzadas y mas realistas como hdri, shadow-volume, PPL, raytracing en tiempo real, etc. LUCES OpenGL usa un modelo simplificado para simular la iluminacin en el mundo real que utiliza un algoritmo para calcular los valores de iluminacin en un vrtice determinado (Phong) y otro para difuminar los diferentes valores a travs de la superficie de la primitiva (guround). La activacin del modelo de luz se hace mediante: glEnable(GL_LIGHTING); Luego es necesario al menos activar una luz as: glEnable(GL_LIGHT0); La especificacin indica que los fabricantes de hardware deben proveer mnimo 8 luces de este tipo que van desde GL_LIGHT0 hasta GL_LIGHT7. Antes de usar cualquier luz es necesario ajustar algunos parmetros mediante la funcin: void glLight{if}( enum light, enum pname, T param ); void glLight{if}v( enum light, enum pname, T params );

Donde el primer dato corresponde al nmero de la luz a ajustar, GL_LIGHT0 por ejemplo, pname es el parmetro a ajustar y params son los valores del parmetro. Es posible utilizar flotante o enteros y especificar uno por uno, o utilizar un vector. Los parmetros para las luces son:

GL_AMBIENT GL_DIFFUSE GL_SPECULAR GL_POSITION

As por ejemplo el conjunto de llamados para activar la luz 0 se vera as:


PLAIN TEXT
C++:

1. 2. 3. 4.
5.

GLfloat GLfloat GLfloat GLfloat

ambientlight[] difuselight[] specularlight[] lightposition[]

= = = =

{0.5f ,0.5f ,0.5f ,1.0f}; {1.0f ,1.0f ,1.0f ,1.0f}; {1.0f ,1.0f ,1.0f ,1.0f}; {0.0f,100.0f,0.0f,1.0f};

6. 7. 8. 9.

glLightfv(GL_LIGHT0,GL_AMBIENT,ambientlight); glLightfv(GL_LIGHT0,GL_DIFFUSE,difuselight); glLightfv(GL_LIGHT0,GL_SPECULAR,specularlight); glLightfv(GL_LIGHT0,GL_POSITION,lightposition);

Lo que ajustara una luz puntual omnidireccional blanca a media intensidad ambiental y ubicada a 100 unidades sobre el eje Y+. El cuarto parmetro de la posicin de la luz (w) se puede utilizar para indicar que la luz se encuentra a una distancia infinita (w=0) en la direccin (x,y,z) , que a efectos prcticos hace que todos los objetos de la escena parezcan iluminados por un tipo de sol o fuente de rayos paralelos similar. Esta ultima particularidad es muy utilizada por los modeladores comerciales para tener una buena percepcin de los objetos en la escena. Es posible especificar un tipo de luz que no sea omnidireccional, es decir un spot, que es asimilable con la luz de una linterna o luz de estudio, que se difunde formando un cono.Para hacerlo basta modificar las siguientes propiedades de la luz:

GL_SPOT_DIRECCION : Un vector de 4 componentes que indica en coordenadas de vista la direccin de la luz GL_SPOT_EXPONENT : Es un entero o flotante entre 1 y 128 que indica la atenuacin de la luz a medida que aumenta el ngulo con respecto al foco central de la luz. GL_SPOT_CUTOFF : Indica el ngulo mximo al cual el spot ilumina. Los valores son entre 0 y 90.

Este tipo de iluminacin es raramente utilizada debido a que su calidad depende de la cantidad de polgonos que posea el modelo a iluminar, entre mayor sea la cantidad el efecto de una luz suave y uniforme ser mejor, pero as mismo caer el rendimiento en general.

Superfices e Iluminacion en OpenGL


MATERIAL En OpenGL es posible especificar el un material por cada lado de un polgono. Para determinar cual es el derecho y el revs de un polgono es necesario inicializar el winding (CCW o CW). Adicionalmente a suministrar los vrtices de la primitiva es ahora necesario suministrar una normal a la superficie dentro de glBegin/glEnd: void glNormal3{bsifd}( T coords ); void glNormal3{bsifd}v( T coords ); Para aquellos que no han visto o se saltaron los cursos de matemtica vectorial una normal a un plano es un vector 3d perpendicular a este y la normal de una superficie en un punto determinado es un vector 3d perpendicular al plano tangente de la superficie de este punto. Para el caso del plano (triangulo, rectngulo o cualquier polgono plano) el clculo de la normal es trivial ya que corresponde al producto cruz de dos vectores cualquier (no paralelos) que se encuentren en el plano as:

Es recomendable entregar a OpenGL estos vectores nomalizados (norma 1), aunque es posible entregarlos de cualquier magnitud y dejar que el API realice el respectivo proceso de escala activando el estado GL_NORMALIZE , aunque esto puede afectar ligeramente el rendimiento. Es posible especificar una normal diferente para cada vrtice y OpenGL se encarga de interpolar estos valores a lo largo de la primitiva. Esto es bastante til, especialmente a la hora de simular superficies curvas utilizando pocos polgonos. En tcnicas mas avanzadas como normal maps se utiliza una textura completa que indica como se deben elaborar normales per-pixel. De la misma manera en que en la luz era necesario especificar los parmetros de emisin difusos, ambientales y de brillo, en el caso de los materiales es necesario especificar estos mismos, solo que ahora son de absorcin. Esto se hace mediante el llamado a las funciones: void glMaterial{if}( enum face, enum pname, T param ); void glMaterial{if}v( enum face, enum pname, T params );

enum face : Indica la cara en que se esta cambiando la propiedad del material, los valores pueden ser GL_FRONT, GL_BACK y GL_FRONT_AND_BACK. enum pname : Indica la propiedad a cambiar, los valores son GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR Y GL_SHININESS. Este ultimo parmetro corresponde a un numero flotante entre 0.0 y 128.0 e indica que tan amplio debe ser el brillo sobre la superficie, un valor pequeo indica que el brillo ser sobre zonas reducidas. T params : Corresponde a los parmetros, pueden variar desde 1 hasta 4.

Este demo muestra algunos materiales sobre la famosa tetera, todos estn bajo una luz con w=0, blanca y de intensidad media. Los diferentes parmetros con que fueron creados se encuentran en el cdigo fuente del ejemplo. El primer material (plstico negro) corresponde a las propiedades: PLAIN TEXT
C++:

1. const GLfloat plasticoNegroAmb[4] = {0.0f,0.0f,0.0f,1.0f}; 2. const GLfloat plasticoNegroDif[4] = {0.01f,0.01f,0.01f,1.0f}; 3. const GLfloat plasticoNegroSpe[4] = {0.5f,0.5f,0.5f,1.0f};

4. GLfloat plasticoNegroShi = 32.0f;

5. 6. //Y se deben ajustar asi antes de dibujar la tetera: 7. 8. //Plastico Negro 9. glMaterialfv(GL_FRONT, GL_AMBIENT, plasticoNegroAmb); 10. glMaterialfv(GL_FRONT, GL_DIFFUSE, plasticoNegroDif); 11. glMaterialfv(GL_FRONT, GL_SPECULAR, plasticoNegroSpe); 12. glMaterialf(GL_FRONT, GL_SHININESS, plasticoNegroShi);