Utilizando la técnica descrita en el artículo anterior se ha representado el contenido espectral de una canción en una estructura tridimensional. Para ello se representa el espectro de cada pequeño tramo temporal de la canción en la cara frontal del cubo desplazándose en profundidad a medida que pasa el tiempo y va llegando nuevo contenido espectral.
El resultado se observa a continuación aunque la calidad del video no es demasiado buena y se observan inconsistencias en el framerate que ‘estropean’ la continuidad del efecto.
Cada plano del eje Z o de profundidad representa a un espectro. Cada espectro se representa en cada plano X-Y del volumen modificándose ligeramente de forma aleatoria el contenido del eje Y o altura con objeto de mejorar la estética del efecto. Con el mismo objetivo se aplica una pequeño efecto de escala al volumen que representa la energía promedio del espectro del intervalo de la canción que esta sonando en ese instante.
El sonido pertenece a un fragmento de uno de los episodios de la cuarta temporada de Battlestar Galactica y ha sido extraído de aquí.
La representación volumétrica es una técnica que permite la representación de campos escalares tridimensionales. Sus aplicaciones son múltiples: representación de imágenes del cuerpo humano procedentes de diversos instrumentos sensores con fines médicos, representación realista de nubes u otros fenómenos gaseosos en simulaciones visuales en entornos virtuales, o representación de diversas estructuras y constructos en la ciencia y la ingeniería.
En este artículo se describirá una variante de la técnica basada en texturas conocida como ‘volume ray casting’. Conceptualmente dicha técnica consiste en el lanzamiento de rayos con origen en la cámara de tal forma que atraviesen el volumen que contiene el campo escalar. El color resultante que se ‘pintará’ en la superficie del volumen en el punto de entrada de cada rayo será una función de los puntos que el rayo atravesó al pasar por el volumen.
Para su implementación se utilizará la plataforma XNA y se hará uso de la aceleración grafica que nos proporcionan la GPU. Será necesaria una tarjeta que soporte al menos la versión 3.0 del modelo de sombreadores (Shader Model 3.0).
El proceso comienza con la representación de un volumen que actuará como frontera del campo escalar, es decir, el campo escalar se encontrará completamente en el interior de ese volumen. Utilizaremos un cubo.
En primer lugar representaremos las posiciones de entrada del rayo y de salida en dos texturas que posteriormente utilizaremos para determinar la dirección. Al Vertex Shader en la GPU se le harán llegar las posiciones sin transformar de los vértices del cubo. Dichas coordenadas se copiaran a la variable InterpolatedPosition a la salida del Vertex Shader. Dicha variable está marcada como coordenadas de textura por lo que a la entrada del pixel shader se recibiran las coordenadas interpoladas. Lo mismo se hará para la variable InterpolatedTransformedPosition solo que en este caso almacenaremos las coordenadas transformadas por la combinación de las matrices de posicionamiento en el mundo (world), vista (view) y proyección (projection) que serán suministradas a la entrada del programa de la GPU como parámetros. Esta última variable será utilizada posteriormente para localizar la posición exacta donde se deben muestrear las texturas para obtener la dirección del rayo.
Se representarán las caras frontales del cubo en una textura utilizando el estado de representación CullMode.CounterClockWise. El color asignado a la textura en realidad contiene las coordenadas tridimensionades del cubo en el espacio de coordenadas local del modelo en el formato (x,y,z,1).
A continuación se repite la operación pero esta vez representando las caras interiores del cubo.
El siguiente y ultimo paso es la representación del cubo, solo que en esta ocasión el color resultante será función del campo escalar, muestreado en base a las direcciones grabadas en las texturas anteriores. Al muestrear cada una de las texturas anteriores podemos obtener para cada punto (x,y) de la textura, las coordenadas (x,y,z) del cubo por las cuales entró el rayo y por las cuales salió. Con estas dos coordenadas se puede calcular la dirección del rayo y al conocer el punto inicial y la dirección del rayo que atraviesa el campo escalar se puede recorrer cada uno de los puntos de ese campo para calcular su contribución al color de la superficie del cubo. Este proceso se realiza en el Pixel Shader que representará el resultado final en pantalla.
13float3 direction = normalize(backPos – frontPos);
14//Inicializamos las variables del color
15float4 color = float4(0, 0, 0, 0);
16float4 src = 0;
17float value = 0;
18//Recorremos el campo escalar en la dirección calculada
19//acumulando opacidad en función del campo
20float3 Step = direction * (1.0f/256.0f);
21for(int i = 0; i < 256; i++)
22 {
23//muestreamos la textura
24 value = tex3Dlod(VolumeTextureSampler, currentPosition).r;
25 src = (float4)value;
26//Front to back blending
27 src.rgb *= src.a;
28 color = (1.0f – color.a)*src + color;
29//advance the current position
30 currentPosition.xyz += Step;
31 }
32return color;
33 }
El rendimiento de esta técnica al ser completamente acelerada por GPU es muy bueno como se puede constatar en los siguientes videos obtenidos con una tarjeta gráfica de gama media/baja con un consumo de CPU mínimo.
Las extensiones de terreno son uno de los fenómenos naturales más comúnmente representados gráficamente por computador.
El algoritmo Diamond-Square (Diamante-Cuadrado) es un método utilizado para la generación de terreno de forma aleatoria. Fue desarrollado por Loren Carpenter, cofundador y científico jefe de Pixar, para ser usado en el modelado de una superficie planetaria.
Los resultados producidos por dicho método se pueden observar en el siguiente video, generado por un programa cuyo código fuente puede encontrar adjunto a este artículo.
Para modelar el terreno se comienza con una matriz de dimensiones mxn, donde típicamente m será igual a n ya que esto simplifica las cosas. Las dimensiones de la matriz deberán ser potencia de dos más 1, es decir, 33×33, 65×65, 129×129, 257×257, etc.
Los valores de la matriz se interpretan como la altura que tiene el terreno en ese punto, por lo que un valor en la posición 30,30 de 65 significa que en la posición 30,30 del terreno, su altura es de 65 unidades. Este tipo de matrices se conocen como ‘heightmap’ o mapa de alturas.
La siguiente figura, extraída de la publicación original, muestra el proceso de generación de las alturas para una matriz de 5×5.
El proceso comienza con las cuatro esquinas, marcadas como 0, es decir, con un ‘cuadrado’ cuyos valores de altura son inicializados a los valores de altura que deseemos, en nuestro caso serán inicializados con el valor 0.
A continuación, se utilizan dichos valores para calcular la altura del punto 1a. Las etapas del algoritmo marcadas como a (1a, 2a, 3a, etc.) forman la parte ‘diamante’ del proceso, ya que si trazamos las diagonales desde cada punto a cada una de las esquinas del ‘cuadrado’ se formara un patrón de rombos, símbolo que habitualmente se utiliza para representar a los diamantes.
Después se procede a la etapa ‘cuadrado’ por lo que se toman las esquinas de cada uno de los rombos generados para calcular un nuevo valor para su centro, puntos marcados como 1b.
Una vez realizadas las etapas diamante y cuadrado podemos observar que la matriz o rejilla ha quedado dividida en 4 cuadrados.
El algoritmo continúa entonces con una nueva fase diamante-cuadrado, marcada como 2ª y 2b, solo que esta vez partiremos de 4 cuadrados en lugar de 1 y a cuya resolución la rejilla quedará dividida en 16 cuadrados.
En este caso el proceso ha concluido porque todos los puntos de la matriz han sido asignados. Si hubiéramos comenzado con una matriz de dimensiones mayores el proceso continuaría con nuevas etapas diamante-cuadrado hasta que quedase totalmente dividida y todos sus puntos hubiesen sido asignados con su correspondiente valor de altura.
Para calcular la altura de cada punto se utiliza el promedio de los valores circundantes, es decir, los cuadrados en la etapa diamante, y las esquinas de los rombos en la etapa cuadrado. A dicho valor, posteriormente se le añade un valor aleatorio (entre –d y +d) que estará limitado por unos parámetros.
Puede observar los detalles en la implementación adjunta a este artículo. Dicha implementación está basada en el programa de Paul Martz publicada en Generating Random Fractal Terrain y utiliza XNA para la representación grafica.
Según cuenta Edward Norton Lorenz en su libro, la esencia del caos, el origen de esta expresión como símbolo del caos es un poco oscuro. Parece haber surgido a raíz de un artículo que presentó en Washington en 1972 titulado “¿Puede el aleteo de las alas de una mariposa en Brasil generar un tornado en Texas?”.
En dicho artículo, Lorenz evitó responder esta cuestión, pero apuntó que si el aleteo de una mariposa pudiera generar un tornado que de otra forma no se hubiera producido, también podría prevenir un tornado que de otra forma se hubiera formado.
Un probable origen de la expresión es debido a una peculiaridad del primer sistema caótico que Lorenz estudió en detalle, conocido como atractor extraño de Lorenz o atractor de Lorenz, cuya representación se muestra en el siguiente vídeo.
Sin embargo, un relato corto de Roy Bradbury, “Un sonido de trueno”, escrito mucho antes de la reunión de Washington, narra la historia de como debido a la extinción de una mariposa prehistórica se cambia el resultado de unas elecciones presidenciales en la actualidad.
Se puede encontrar el código fuente que generó el vídeo de este artículo adjunto, el cual esta basado en uno de los ejemplos que se proporcionan con XNA Game studio 2.0.
Resulta interesante observar como partiendo de un estado inicial y de unas reglas sencillas, el juego de la vida evoluciona exhibiendo complejos patrones.
Ejemplo de autómata celular, concebido por John Horton Conway y popularizado por Martin Gardner en un artículo de Scientific American aparecido en 1970, el juego de la vida, desde su aparición, ha sido plasmado por los programadores en multitud de sistemas.
Utilizando la plataforma XNA y tomando como base uno de los ejemplos que se proporcionan consistente en un sistema de partículas 2D, se realizó una implementación del juego de la vida. La implementación funciona tanto en sistemas Windows como en Xbox 360 suponiendo que contemos con una licencia para ejecutar código en dicha consola. Para compilar el código es necesario disponer de XNA Game Studio Express 1.0 Refresh, el cual se puede descargar gratuitamente.
En el siguiente vídeo se puede observar la evolución del autómata celular para un estado inicial concreto establecido al azar.
La implementación consta de una rejilla de 50×50 celdas o ‘células’. Cada célula puede estar en uno de dos posibles estados: viva o muerta.
Para cada iteración se calcula el siguiente estado utilizando las reglas descritas a continuación:
una célula ‘nace’, es decir, pasa al estado de viva si y solo si tres células vecinas suyas están vivas.
Si una célula viva tiene como vecinas a dos o tres células vivas, seguirá viva. En caso contrario la célula morirá.