Buscar este blog

sábado, 13 de noviembre de 2010

Mi primer blur

Bueno, tan pronto como traducí mi aplicación a openGL, ya le añadí el blur. Por ahora el resultado es pobre porque solo me queda perfilar los efectos, pero la dinámica de trabajo, que es lo que me interesaba, ya la conozco. Este es el resultado final:




La visualización es pobre por dos motivos: no hago cálculos de qué color corresponde a cada nodo, ya que todavía estoy viendo como pintar árboles, no cuál es el contenido del árbol, cosa de la que depende el color asignado (y cuestión todavía que tengo que definir). Y las aristas tampoco tienen blur aún (y tampoco se han colocado sombras).

Estos aspectos se definirán ya muy a posteriori, mi siguiente prioridad ahora es programar el algoritmo de visualización del grafo (es decir, el que coloca cada nodo en un lugar adecuado para una estética visualización), y luego, pelearme con la impresión de texto.
Leer más...

Adios Ogre

Desde mi última entrada, han ocurrido muchas cosas en mi proyecto, pero qué han tenido muy poca repercusión en su aspecto actual. Como ya adelanté en la presentación, quería que mi proyecto se pareciera al aspecto de Gource por cuestión de elegancia, y todos éstos cambios tienen su origen en la persecución de ésta idea.

Como un grafo, y por tanto un árbol, no es más que nodos y aristas, los modelos 3D con los que hace falta trabajar son muy sencillos. Sumándole ésto a que el tamaño de cada nodo, o de cada arista, puede ir cambiando en tiempo de ejecución, el dibujado, y con ello los modelos también deberían hacerlo. Ésto me llevo directamente a trabajar con el tipo ManualObject de Ogre, que te permite definir los modelos vértice a vértice, definir subobjetos, y cambiarlos en tiempo de ejecución. Es decir, todo lo que necesitaba, aunque precisamente por trabajar con objetos geométricos a bajo nivel veía a Ogre un recurso demasiado disparatado. Ogre está orientado para trabajar con modelos complejos, en escenarios complejos, con cálculos complejos. Yo no necesito muchos efectos gráficos ni de iluminación, y es todo relativamente sencillo. Pero por tal de no empezar a aprender openGL, por cuestión de tiempo, decidí seguir adelante.

La primera idea para simular el efecto de Gource era mediante el uso de una esfera, con una luz en su centro que la reflejara, para que tuviera el aspecto de una estrella brillante. Cómo se nota que era la primera vez que trabajaba en un contexto 3D. Cuándo tienes un objeto, iluminarlo solo cambia la intensidad de la luz en su superficie, en los lugares donde la luz incida, oscureciéndose a medida que la superficie se aleja de la luz, o se hace más innacebile para ella. Una luz no se verá si no hay ningún objeto en el que reflejarla. Y por poner una luz dentro de un objeto, nunca va a tener un aspecto de "bombilla".

Una vez comprendido ésto, cambié de táctica. Relegé la construcción 3D del árbol en un nuevo objeto, usando el patrón estrategia (StrategyMakeTree). Ahora tenía dos construcciones, una mediante esferas (SphereMakeTree), y otra recurriendo al llamado efecto Tyndall (ColloidMakeTree).

El efecto Tyndall es lo que ocurre cuando un rayo de luz entra en una habitación, o cuando el agua se refleja en el mar. El aire de la habitación, o el agua, tienen una serie de impurezas. Cuando un rayo de luz penetra, cada partícula impura refleja la luz que incide en ella, creando una serie de puntos de luz indistinguibles entre sí para el ojo humano, dando la sensación de un continuo iluminado. Luz reflejada en aire o agua pura no es visible en forma de rayo, debido a que no tiene partículas que puedan reflejar la luz incidente en ellas. En la imágen siguiente se ve un láser que atraviesa dos vasos, uno con agua pura, y otro con impurezas, y se observa como el agua pura no muestra al rayo atravesándolo, mientras que las impurezas disueltas en el agua sí que lo permiten.



Un sistema como el aire o el agua con impurezas, son llamados coloides, y mi nueva idea, era, por supuesto, que cada nodo fuera un coloide. Se crearía una nube de puntos con forma esférica, de forma que en su centro hubiera una mayor densidad, y en el límite de su radio poca. Luego, situando una luz en su centro se simularía un coloide, difuminándose la intensidad lumínica a medida que la luz se alejaba de su centro, hasta diluirse con el fondo negro.

Pero por más formas ideadas para implementar un coloide, ninguna tuvo éxito. Para experimentar, comencé construyendo una esfera que tuviera siempre una misma densidad de puntos. Pero si construía una esfera de poca densidad, se veía en vez de un coloide una nube dispersa de puntos. Si la construía con poca densidad. Se veía una esfera sólida en vez de un coloide. Si hacía una esfera de poca densidad, con mucho radio, y alejando mucho la cámara, se volvía a ver una esfera sólida.

Mi siguiente paso fue ver como lo hacía Gource. Me llevé un tiempo estudiando su código fuente, lo que me llevó a aprender bastantes cosas de openGL para comprenderlo. Explorándo el código descubrí, entre otras cosas, que el efecto de destellos de luz cuando se creaba una rama nueva se llama bloom. El desenfocado que buscaba, blur (blur no es más que borroso en inglés). También descubrí que existe una rama de la teoría de grafos que se llama visualización de grafos, y Gource usa un algoritmo de visualización basado en fuerzas, esto es, que el grafo funciona como un sistema gravitatorio, donde cada nodo afecta a la posición de los restantes nodos, hasta alcanzar una posición de equilibrio, que es la posición final de visualización.

Otro detalle interesante es que, como el árbol está cambiando de posición y orientación constantemente, las aristas están curvadas de modo que incrementen la sensación de movimiento, curvándose la aristas en un sentido semejante al sentido de giro del árbol. Para construir estás aristas curvadas, se hace uso de splines.

La forma que tiene gource de dibujar el blur (el desenfoque), es mediante el uso de texturas. Se especifican las coordenadas de un cuadro, se coloca encima una imágen que contiene un círculo difunado que hace las veces de desenfoque, y encima se coloca otra imágen con el nodo en cuestión. El color de la textura de desenfoque se mezcla con el del nodo, y con eso finalmente parece que es el propio nodo el que brilla.

Intenté hacer lo propio con Ogre, creando la clase GourceianMakeTree. Y empezaron los problemas. Por más vueltas que le daba, no conseguía que la textura se renderizara bien. Sobre el funcionamiento interno de ManualObject existe poca documentación, en la lista de correo de la forja tampoco hubo nadie que me pudiera ayudar de forma totalmente satisfactoria. Incluso un desarrollador con el que mantengo contacto por correo, muy familiarizado con Ogre y openGL, con el que aprendí muchísimo, tampoco supo como resolver mi problema. Todo ello demuestra el poco conocimiento, uso o divulgación, de los ManualObject, lo que me convenció más aún que Ogre no estaba hecho para trabajar de esa forma (ya que nadie más lo hacía).

Así que decidí dejar Ogre, y con ello OIS, y cambiarme a openGL, con Glut primero, y con SDL después. Este paso de glut a SDL fue debido a que, aunque glut sea orientado a eventos -cosa que hace más elegante el diseño para este tipo de aplicaciones-, su sintaxis me obligaba a hacer cosas poco elegantes, como tratar con atributos externos o trabajar con referencias a funciones miembro, con sintaxis prefijada, y sin tener control total sobre la función MainLoop, que es la función de Glut que lleva el control de toda la aplicación.

Una vez perdidos unos cuantos dias aprendiendo openGL, pude finalmente reescribir mi aplicación. El diseño prácticamente no ha cambiado. Las clases se organizan de la misma forma, solo que las funciones miembro sí que son distintas y hacen cosas distintas.

Un detalle curioso (que tuve que descrubir yo, y nunca se advierte cuando se habla de SDL + openGL), entre Glut y SDL es que, mientras Glut toma el sistema de coordenadas del escenario centrado en la pantalla, SDL toma el centro del escenario en la coordenada (0, 0) de la pantalla, es decir, la esquina superior izquierda. Además, la pantalla se convierte en el primer cuadrante invertido, ya que la coordenada y de la pantalla crece desplazándose hacia abajo, como de costumbre en SDL. Es decir, que hay que tener en cuenta que SDL no cambia su comportamiento sea una ventana openGL o una pantalla "clásica".

Y respecto al aspecto actual del proyecto, pocas novedades. Todavía no tengo programado el desenfoque, solo pinto un cuadro blanco por cada clado, conectados por aristas. En la siguiente imágen se muestra una captura.



No hay ningún algoritmo "propio" de impresión del árbol. Sencillamente, los hijos los imprimo en columna, con un desplazamiento x constante, por tanto, los hijos de padres distintos podrían solaparse.
Leer más...