MUNDO

jueves, 9 de febrero de 2012

Amigos les dejo un concepto y un poco de informacion de una herramienta muy buena es open GL

OpenGL : Una librería estándar.
Afortunadamente para nosotros ya existen librerías que se encargan de hacer todo lo que mencionamos anteriormente de una manera eficaz. En particular vamos a estudiar la librería OpenGL que es una poderosa interface de software para hardware gráfico, desarrollada por Silicon Graphics y que cada vez se vuelve más un estándar. Podemos encontrar OpenGL en diversas plataformas que van desde las estaciones de trabajo, hasta las PC que trabajan con windows NT.
OpenGL es una interface que consiste de aproximadamente 120 comandos, que se utilizan para especificar los objetos y las operaciones necesarias para producir aplicaciones tridimensionales interactivas.
Esta librería está diseñada para trabajar eficientemente aunque la computadora en la cual se despliegan las gráficas no sea la computadora que está corriendo el programa de gráficos. Esto sucede por ejemplo en un entorno de red. La computadora en la cual corre el programa se llamará cliente y en la cual se realiza el despliegue es el servidor (esto, como podemos ver es idéntico que para X). Esto es útil por ejemplo al hacer los cálculos en una computadora poderosa y el despliegue en una PC.
OpenGL utiliza un protocolo que es el mismo para todas las máquinas que trabajan con esta librería así que los programas OpenGL pueden trabajar a través de la red aunque el cliente y el servidor sean distintos tipos de computadoras. Si no se trabaja a través de una red entonces la misma máquina actúa como cliente y servidor a la vez.
OpenGL esta diseñada como una librería independiente del hardware para lograr esto NO tiene comandos para realizar tareas de manejo de ventanas o de entradas del usuario (por ello utilizamos Xforms), tampoco provee comandos de alto nivel para describir modelos de objetos tridimensionales complejos, como podrían ser un automóvil o una casa. Lo que si tiene OpenGL es un conjunto de primitivas geométricas : puntos, líneas y polígonos, así como comandos que actúan sobre estas primitivas y que permiten realizar tareas como Sombreado Gouraud, iluminación y texturizado.
Siguiendo la misma filosofía de construir en `capas' como hemos estado viendo hasta ahora, podríamos pensar en la posibilidad de construir encima de OpenGL una librería que nos permitiera manejar objetos más complejos, por ejemplo, y de hecho, esta librería existe y se llama Open Inventor, y está disponible para las estaciones Silicon Graphics.
Es interesante notar que OpenGL aprovecha al máximo el hardware de las plataformas a las cuales ha sido portado, así es que por ejemplo en la Silicon Graphics, hará uso de los circuitos especializados para gráficos, por esto es una librería muy veloz en esta plataforma.
Dentro de las diversas funciones que puede realizar OpenGL sobre las primitivas, podemos mencionar:
  • Descripción y despliegue de primitivas tales como puntos, líneas y polígonos, así como el cálculo de sus normales.
  • Manejo del `objeto' y de la `cámara' en un espacio tridimensional. Dentro del manejo se incluyen transformaciones tales como rotaciones, escalamientos, proyecciones...
  • Creación de listas de despliegue, esto es la agrupación de comandos de OpenGL, que posteriormente pueden ser llamados fácilmente (podríamos hacer una analogía con una macro).
  • Iluminación, posicionamiento de fuentes de luz locales y ambientales. Y algunos más avanzados como son:
  • Blending (mezclado), Antialiasing (antiseudónimos) y neblina.
  • Despliegue de pixeles, Bitmaps (mapas de bits), Fuentes e Imágenes.
  • Mapeo de texturas o texturizado, esto es la aplicación de una Imagen o de un Bitmap a alguna de las primitivas. Existen más opciones pero no las analizaremos, para más detalles consultar el libro: OpenGL Programming Guide (Jackie Neider, Tom Davis y Mason Woo, OpenGL ARB, editorial Addison Wesley).
    Vamos a dar una breve introducción a algunos de los comandos de OpenGL así como una explicación, para preparar al lector de tal forma que pueda poner en práctica los comandos desde un principio.
    Es necesario notar que todos los comandos de OpenGL comienzan con el prefijo gl (graphics language) y las constantes definidas con el prefijo GL. Algunos comandos de la librería pueden recibir diversos tipos de datos, y se pondrá un asterisco para hacerlo notar, por ejemplo para el comando glVertex*(), el asterisco significa que existen las variantes glVertex {2,3,4} {s,i,f,d} [v] (TYPE coords).
    - 2,3,4 : Si se quieren 2 , 3 o 4 datos, por ejemplo el usar 2 hace que se tomen las coordenadas de z=0.
    - s,i,f,d : Si se quieren usar coordenadas short , int , float o bien double.
    - v : Si se quiere usar las coordenadas como un arreglo.
    7.- Primitivas Gráficas.
    Enseguida veremos cuales son los pasos a seguir para realizar el despliegue de un objeto en 3 dimensiones, y conforme avancemos, veremos los comandos necesarios para lograrlo.
    Dentro del ejemplo que vamos a tomar, usaremos el siguiente objeto:
      Como podemos ver en la fig 1. El objeto que vamos a tratar aquí es un cubo sencillo. Sabemos que un cubo tiene por definición 6 caras o facetas distintas. De ahí que el primer paso que debemos hacer cuando queremos programar en OpenGL es conocer los puntos que definen las facetas de nuestro objeto. En este caso son puntos muy sencillos y es fácil imaginar un cubo en tres dimensiones pero resulta imposible determinar los puntos que definen por ejemplo la carrocería de un automóvil con tan solo la imaginación! Es por eso que existen programas que permiten crear figuras en tres dimensiones y que escriben código de OpenGL.
    De aquí en adelante usaremos los términos polígono y faceta de manera idéntica.
    Cuando creamos un objeto en 3 dimensiones con la librería OpenGL, tenemos la opción de escoger diversas primitivas para construirlo. Sin embargo tenemos que tomar en cuenta algunas restricciones:
  • Los bordes de los polígonos no pueden intersectarse.
  • Los polígonos deben ser convexos.
  • Los puntos que forman un polígono deben estar en un mismo plano. El único polígono que nos garantiza esto es el triángulo, de hecho una gran parte de los programas de 3D trabajan con triángulos nada más. Dentro de las primitivas que permite utilizar OpenGL se encuentran las siguientes:
    GL_POINTS Dibuja puntos.
    GL_LINES Dibuja líneas no conectadas.
    GL_POLYGON Dibuja un polígono de n vértices donde n es como mínimo 3.
    GL_TRIANGLES Dibuja una serie de triángulos.
    GL_LINE_STRIP Dibuja una serie de líneas interconectadas.
    GL_LINE_LOOP Igual que el anterior pero el primer y último están interconectados.
    GL_QUADS Dibuja una serie de cuadriláteros.
    GL_QUAD_STRIP Dibuja cuadriláteros pegados unos con otros.
    GL_TRIANGLE_STRIP Dibuja triángulos pegados unos con otros (uno frente a otro).
    GL_TRIANGLE_FAN Dibuja triángulos pegados unos con otros en forma de abanico.
     
     
    La información más importante acerca de los vértices son sus coordenadas, que se especifican con el comando glVertex*(). Por cada uno de los vértices se puede especificar un color, un vector normal y coordenadas de textura. Al querer definir un polígono, debemos hacerlo las llamadas a glVertex*() exclusivamente dentro de los limites que fijan las instrucciones: glBegin() y glEnd(). Para la asignación de colores usaremos el comando glColor*(). Es también necesario saber que un polígono tiene dos caras, se define la cara anterior como aquella para la cual los vértices van en el sentido contrario al de las manecillas del reloj.
    Es así como para nuestro programa vamos a hacer los comandos siguientes para cada una de las caras del cubo:
    glBegin(GL_POLYGON);
            glColor3f(0.0,0.0,0.0);
            glVertex3f(1.0,1.0,1.0);
            glVertex3f(1.0,1.0,-1.0);
            glVertex3f(-1.0,1.0,-1.0);
            glVertex3f(-1.0,1.0,1.0);
    glEnd();
      Si nos fijamos en la figura 1. anteriormente mostrada, podemos apreciar que se trata de la cara superior del cubo, y que su color es negro. Es necesario notar que OpenGL es una máquina de estado, esto significa que se puede poner en diversos estados o modos que permanecen hasta que uno pide su cambio. Por ejemplo, el color actual es una variable de estado, se pone en un cierto valor y este permanece para las funciones que hacen uso del color hasta que no se vuelve a llamar a glColor*(). Existen más variables que controlan cosas como los modos de dibujo de los polígonos, el posicionamiento de las luces, y las propiedades materiales de los objetos que están siendo dibujados. Se activan y desactivan mediante llamadas a glEnable() y glDisable(), aunque todas las variables de estado tienen un valor predefinido.
     
     
    8.- Limpiando la pantalla. Aunque hemos visto en la sección anterior los pasos necesarios para la `definición' de las facetas de un objetos tridimensional, antes de comenzar a dibujar cualquier cosa en la pantalla, debemos primero limpiarla, en el caso de OpenGL, lo que vamos a hacer es limpiar los buffers principales.
    Entre los diversos buffers que existen, debemos conocer principalmente dos de ellos: el buffer de color que contiene el valor del color de los pixeles que son desplegados, y el buffer de profundidad. Este último es muy importante pues contiene el valor en el eje Z de cada uno de los pixeles que hay en la pantalla, así que si se desea dibujar un nuevo objeto, el valor en el eje Z de sus puntos se compara con el que existe en el buffer de profundidad, y si sus puntos se hallan más `próximos' a la pantalla que los que existían anteriormente, entonces se dibujan los puntos del objeto, de lo contrario, esto no se realiza.
    El `limpiado' de los buffers se realiza de maneras diferentes para cada uno de los mencionados, pues para el de color, basta hacer una llamada a glClearColor() , dando el color con el cual se desea limpiar la pantalla y después a glClear() para hacer el limpiado. El buffer de profundidad realmente no se `limpia' sino que más bien se llena con un valor de profundidad así que una vez que esto se realiza, si los siguientes objetos están igual o más cerca de la pantalla que los que se llenaron con el valor dado, siempre serán dibujados. Normalmente se llena con el valor máximo de profundidad. Para limpiar los buffers de una sola llamada se puede hacer lo siguiente:
    glClearColor(0.0,0.0,0.0,0.0); /* El color de limpiado será cero */
    glClearDepth(0.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
     
    En nuestros programas haremos uso de la técnica de doble buffer esto para poder realizar animaciones de nuestros objetos. El empleo de dos buffers nos permite dibujar una imagen mientras se despliega la otra, así mientras una vista del objeto se muestra, la siguiente se calcula, y así al intercambiar las imágenes, tenemos la impresión de que estamos viendo una animación.
    9.-Visualizando.
    Una vez que hemos hablado de las primitivas gráficas, así como de los buffers en los cuales estas se presentarán, debemos estudiar la visualización que es el otro aspecto esencial a considerar. Para visualizar un objeto en 3 dimensiones, podemos hacer una analogía con la actividad de tomar una fotografía, pues básicamente se siguen los mismos pasos. En el primero se pone la cámara apuntando a la escena que queremos tomar, después posiblemente modifiquemos algunas cosas de la escena a ser fotografiada, luego ajustamos el acercamiento, y finalmente se escoge de que tamaño se quiere la impresión final. Estos mismos pasos tienen nombres específicos en el campo de la visualización 3D, pero aquí se habla de transformaciones. Siguiendo el mismo orden que citamos anteriormente, vendrían siendo: la transformación de vista (viewing), la de modelado (modeling), la de proyección (projection) y por último la del puerto de vista (viewport) .
    fig3. Etapas de transformación de los vértices.
      Las transformaciones de vista deben preceder las de modelado forzosamente, las demás pueden ser especificadas en cualquier momento, antes de que se haga el dibujo. Los vértices se componen de 4 coordenadas, estas son (x,y,z,w). Si la coordenada w es diferente de cero, entonces las coordenadas corresponden al punto tridimensional (x/w,y/w,z/w). La coordenada w se usa poco, y si no se especifica, toma el valor 1 por omisión.
    Cada vértice de la escena es multiplicado por una matriz de tamaño 4x4 que se construye a partir de las transformaciones de vista, modelado y proyección. Las normales se ven afectadas automáticamente por esta operación. Las coordenadas de recortado crean un volumen de vista, en el cual sólo los objetos que se hallan en su interior se toman en cuenta. Y finalmente las coordenadas ya transformadas se convierten en coordenadas de ventana, el puerto de vista puede ser manipulado causando modificaciones a la imagen final tales como agrandamiento, reducción o estiramiento.
    Vamos a estudiar cada una de estas transformaciones individualmente ya que es necesario aplicarlas correctamente para obtener una imagen aceptable en la pantalla. Es importante limpiar la matriz actual o de trabajo, antes de hacer modificaciones, de lo contrario pueden obtenerse resultados equivocados si la matriz de trabajo contiene operaciones realizadas anteriormente. Para esto se utiliza el comando glLoadIdentity().
    Antes de realizar transformaciones, debemos especificar con cual de las matrices vamos a estar trabajando, para esto, se usa el comando glMatrixMode() con uno de los argumentos siguientes: GL_PROJECTION o bien GL_MODELVIEW, dependiendo de la que necesitemos.
    La transformación de vista:
    Como hemos visto anteriormente esta transformación es análoga al posicionamiento y orientación de la cámara. Es importante notar que en OpenGL, la cámara así como los demás objetos de la escena aparecen en el origen, y la cámara apunta hacia el eje z negativo. Se pueden usar funciones como glTranslate*() o glRotate*() para hacer modificaciones. Esta transformación se aplica a la proyección, usaremos entonces: glMatrixMode(GL_PROJECTION).
    La transformación de modelado:
    Esta transformación se utiliza para posicionar y orientar el modelo u objeto que vamos a visualizar, se puede rotar, trasladar o cambiar la escala del modelo (scaling), para las dos primeras operaciones se usan los comandos mencionados en la transformación de vista, para el cambio de escala se usa glScale*(). Es necesario especificar que trabajamos con el modelo, de ahí que usaremos: glMatrixMode(GL_MODELVIEW).
    La transformación de proyección:
    Esta transformación equivale a poner lentes diferentes a la cámara, que cambiarán el campo de vista (es diferente usar un telefoto que un gran angular) además de esto, la transformación determina de que forma se proyectarán los objetos en la pantalla, existen dos maneras de realizarlo, la primera es usando perspectiva que hace cambiar el tamaño de los objetos dependiendo de la distancia a la cual están con respecto al observador, para esto se usa el comando glFrustum(). El otro tipo de proyección es ortográfica en la cual el tamaño de los objetos no cambia (esta proyección se usa comúnmente en aplicaciones CAD).
    La transformación de puerto de vista:
    Esta transformación define el tamaño y la posición de la imagen final (como la impresión de una fotografía), se usa el comando glViewPort() y es necesario modificar el puerto de vista cada que la ventana se modifica.
    Una vez que todas estas transformaciones son aplicadas a cada uno de los vértices, la imagen puede ser desplegada dentro de la ventana.
    La sintaxis de los comandos de transformación es la siguiente:
    glTranslate {fd} (TYPE x, TYPE y TYPE z);
    glRotate {fd} (TYPE ángulo,TYPE x, TYPE y, TYPE z);
    glScale {fd} (TYPE x, TYPE y, TYPE z);
    En donde TYPE es float o int, dependiendo del comando que se escogió.
    10.- Normales, Color e Iluminación:
    Ejemplo de llenado suave.
      Cuando hablamos de que cada vértice puede tener atributos propios, mencionamos color y normales. El color puede ser el mismo para los todos los vértices o distinto, lo que hará la diferencia es el tipo de `llenado' de los polígonos. Existen dos `modelos' de llenado, uno uniforme (flat) y el otro suave (smooth o Gouraud), se especifican con el comando glShadeModel() con uno de los argumentos siguientes GL_SMOOTH (que es el que existe por omisión) o GL_FLAT. Si los vértices tienen colores distintos y se escoge el llenado uniforme, se tomara el color de uno de los vértices y se llenara todo el polígono con ese color, si se tiene el llenado suave, los colores irán variando gradualmente entre los bordes del polígono. Recordemos que el color se asigna con el comando glColor3*() cuya sintaxis es la siguiente:
    glColor3 {b,s,i,f,d} (TYPE rojo, TYPE verde, TYPE azul);
    El uso de normales es necesario cuando queremos aplicar iluminación. Es importante notar que cada uno de los vértices tiene una normal, esto tal vez no es algo evidente en un principio cuando pensamos que cuatro vértices que conforman un plano tienen 4 normales distintas, pero veamos la ilustración siguiente:
    fig4. Diferente asignación de las normales.
      Como podemos ver, en la figura de la izquierda, tenemos una sola normal al plano que forman las facetas. De cualquier forma tenemos que dar una normal para cada vértice, lo que hacemos es usar la misma. En el otro caso, cada vértice tiene una normal, que es calculada a partir de la normal de las facetas adyacentes. Al aplicar una fuente de luz, podemos ver la diferencia de resultados, el segundo caso dará la impresión de una superficie más suave, no compuesta por facetas. Asignar las normales es una tarea sencilla a nivel de código pero puede ser algo más complicado el saber sus coordenadas, para esto es necesario recordar lo que se aprendió en los cursos de matemáticas. El comando para agregar normales es glNormal3*(), y su sintaxis es la siguiente:
    glNormal3 {bsidf} [v] (TYPE nx, TYPE ny, TYPE nz);
    Un ejemplo de esto es el siguiente:
    glBegin(GL_POLYGON);
            glNormal3fv(n0);        /* n0 es un arreglo que contiene los datos */
            glVertex3fv(v0);
            glNormal3fv(n1);
            glVertex3fv(v1);
            glNormal3fv(n2);
            glVertex3fv(v2);
    glEnd();
      Una vez que hemos asignado normales a los vértices es posible iluminar nuestro objeto. Existen varios pasos a seguir:
  • Creación, posicionamiento y activación de las fuentes de luz.
  • Selección de un modelo de iluminación.
  • Definición de las propiedades materiales de los objetos en la escena. Las fuentes de iluminación tienen varias propiedades, como el color, la posición y la dirección, se usa el comando glLight*() para especificar los diversos valores. El tema de iluminación se explicara más a fondo durante el taller.
    11.- Para finalizar:
    Todo lo que hemos visto anteriormente nos permite ya tener bases para realizar una aplicación de visualización 3D en la estación de trabajo. No hemos tratado otros aspectos como son el texturizado, pues estos serán tratados durante el taller si hay suficiente tiempo.
    Es importante leer atentamente esta pequeña introducción pues permitirá un avance más veloz durante el taller cuya duración impide hacer mucho énfasis en los puntos que se explican, aunado a la amplitud de los temas tratados.
  • No hay comentarios:

    Publicar un comentario