|
|
Desde los primeros tiempos de la animación 3D se han utilizado muchos trucos para insertar árboles en nuestras escenas. Uno de los más conocidos —utilizado ampliamente en los entornos de los videojuegos— es el de los “billboard trees” consistente en colocar la imagen frontal de cada árbol sobre un plano que siempre apunta hacia la cámara. De ese modo podemos poblar una amplia superficie con decenas o cientos de planos —árboles— que siempre “nos miran” de frente. Una variante ligeramente mejorada consiste en realizar un render frontal y otro lateral y colocarlos sobre dos planos ortogonales —un aspa— o incluso sobre 3 planos a 60º de desfase. En estos últimos casos no haremos que los planos “nos miren” sino que al ir girando en torno a ellos nos irán mostrando los sucesivos 2 ó 3 puntos de vista previamente calculados del árbol. De esta manera conseguiríamos no tener que lidiar con muchos miles o millones de polígonos, ya que un árbol puede llegar a tener una geometría muy densa —si queremos representarlo de un modo detallado y realista— por lo que cuando necesitamos poblar una escena con una gran cantidad de ellos se sobrecarga de tal manera que se hace inmanejable. El problema de los billboards es que, con cierto tipo de movimientos y niveles de acercamiento, el truco se hace muy evidente, arruinando todo nuestro trabajo. Existen soluciones intermedias consistentes en crear versiones de árboles con una cantidad reducida de polígonos: si un árbol muy detallado fácilmente puede pasar de los 100.000 polígonos, podemos crear versiones más ligeras con “sólo” 1.000 o 2.000 polígonos. Pero pese a ello, si necesitamos trabajar con cientos o miles de árboles, nos siguen generando geometrías bastante pesadas. Con el problema añadido de que no podemos acercarnos mucho, ya que enseguida se hace palpable su falta de resolución. Vamos a ver una técnica que lleva un paso más allá la de los billboards, permitiendo acercarnos bastante a cada uno de nuestros árboles durante una animación y girar en torno a ellos usando tan sólo un polígono (bueno, para ser exactos: dos polígonos por árbol). Podríamos definirla como una versión “casera” de la tecnología empleada por los modelos RPC de la compañía Archvision.
(*) NOTA: cuidadín al generar la movie de 360º con el ángulo de giro que hayáis seguido. No es lo mismo seguir una orientación horaria que anti-horaria. En mi caso yo he trabajado en el sentido de las agujas del reloj [ Gracias por el apunte, Alberto ]
Muy bien ¿y qué hacemos ahora con nuestra película de 360 frames filmada alrededor de nuestro árbol?. Pues dicho en pocas palabras:
Eso es todo, así de simple.
Pero vamos a verlo todo con más detalle. En el siguiente gráfico vemos la configuración básica: colocamos un plano donde necesitamos cada árbol y los texturizamos con nuestra movie. Tan sólo necesitamos saber el ángulo con el que miran hacia la cámara para que en cada caso nos muestren el fotograma correspondiente a esa posición (enseguida veremos cómo hacerlo).
La clave del proceso: el script Bien, en todo este proceso que —creo— resulta muy sencillo de entender, sólo hay un pequeño detalle que tendremos que solventar: La respuesta es corta: mediente un sencillo script, una pequeña orden de programación. Prácticamente todos los programas 3D incorporan la posibilidad de utilizar scripts: MEL en Maya, COFFE en Cinema, MAXScripts, etc. Posiblemente muchos de los que leáis este tutorial nunca habéis creado ni utilizado un script. Yo jamás he estudiado ningún lenguaje de programación, pero con un poco de voluntad resulta accesible hacerse con los principios más básicos que nos permitan crear algún script sencillo. Algo que puede sernos de una enorme ayuda e evitarnos muchísimo trabajo pesado cuando trabajamos en 3D, especialmente si estamos creando una animación con tareas repetitivas o que implican lidiar con multitud de objetos. Para el script de este tuto he utilizado Xpressionist, el lenguaje interno de ElectricImage. Explicaré paso a paso las pocas líneas del código para que cada cual pueda interpretarlo y trasladarlo a su correspondiente software. Lo importante es que en cada paso se entienda el concepto, porque está clarísimo que luego podréis hacerlo siguiendo otros caminos (cada programa es diferente y plantea unas paticularidades propias. Y al fin y al cabo, siempre hay muchas formas de llegar al mismo sitio...). Este sería un script básico (e “ideal”, el definitivo habrá que pulirlo un poco, dependiendo del comportamiento de vuestro programa) que haría lo que más arriba hemos visto:
Veamos lo que controla cada una de estas líneas:
Con esta línea estamos ordenando a nuestro árbol Arbol1 que “mire” a la cámara. El giro en el eje Y Yaw_Y del plano que lo contiene estará controlado por la posición relativa de cada elemento —Arbol.Position y Camara.Position—, usando la función lookat y restringiendo ese giro exclusivamente al eje Y. NOTA ACLARATORIA: Supongo que la mayor parte de programas 3D ya llevan incorporado un sistema de constraints que permite que una orden tan sencilla como un LOOK-AT sea controlada de un modo directo, sin tener que recurrir a scripts como éste. ElectricImage, de hecho, también tiene unos potentes constraints que permite gestionar el look-at en tiempo real —entre otras cosas—. Pero me he visto obligado a no utilizarlas y controlar ese parámetro a través de un script, porque de lo contrario los valores angulares que tomaba el plano dirigido por los constraints no eran tenidos en cuenta por el resto del script. No se si se puede considerar a esto como un bug —fallo— o una limitación generalizada... En cualquier caso lo aclaro aquí para que, si os sucede algo similar con vuestro soft favorito, tengáis en cuenta la posibilidad de controlar el look-at opcionalmente mediante scripts.
Una vez que determinamos el ángulo de giro del eje Y en nuestro árbol Arbol1.Yaw_Y calculamos su valor entero y redondeado round para indicarle a nuestra textura animada arbol360movie el número del fotograma frame que debe mostrar en el canal de color Diffuse. Esto es muy importante, ya que nuestro plano puede tener un ángulo de giro con varios decimales (17,1679º) pero lamentablemente no contamos con un frame “17,1679” en nuestra textura animada. Por ello deberemos quedarnos con el valor más aproximado y usar el frame 17.
Esto es básicamente lo mismo: igual que con el canal de color hacemos con el mapa de recorte de nuestro árbol Clip.
Pero las cosas suelen no ser tan simples... Hasta aquí es todo muy “bonito y sencillo”... Pero nos encontramos con un problema bastante lógico en cuanto empezamos a poblar nuestra escena con muchos árboles: se hace evidente que todos ellos son el mismo árbol, porque aunque cada uno de los planos que los soporta tiene un valor de giro específico —como resultado de estar mirando a la cámara—, la realidad es que si nos alejamos un poco, el aspecto en los árboles cercanos es muy similar. Para poder entender y solucionar mejor este problema podemos sustituir la textura animada de un árbol por otra textura mucho más “gráfica”. Una especie de checkgrid animado: generamos una movie de 360 frames haciendo que vaya cambiando de color y recorriendo todo el espectro (esto es muy sencillo de obtener con AfterEffects, por ejemplo). Y de paso aprovechamos para incluir en cada frame un número correspondiente al valor numérico de ese frame. Cuando aplicamos esa textura de chequeo a nuestro conjunto de planos y renderizamos un recorrido enseguida aparece el problema del que hablamos. En la siguiente animación podemos verlo de un modo muy gráfico:
Y por otro lado necesitamos crear un script potente y flexible que nos permita gestionar un número elevado de árboles sin tener que ir indicando árbol por árbol lo que tiene que hacer cada uno. Imaginemos que queremos controlar un pequeño bosque de 100 árboles. Éste será el aspecto que tendrá:
Puliendo el script Veamos una solución que permitirá controlar el comportamiento de todo ese conjunto (una de las muchas posibles, porque seguro que habría otras formas de conseguirlo):
E igual que antes, vamos a ver qué es lo que controlamos en cada apartado:
Ahora el giro en el eje Y de cada uno de los árboles, desde el primero al último, está representado bajo la forma $arbol.Yaw_Y y, de la misma manera que hemos explicado más arriba, viene controlado por su situación relativa respecto a la cámara en todo momento, mediante el uso de la función lookat.
Como ya hemos explicado antes, necesitamos convertir el valor del ángulo resultante a en una cantidad entera, sin decimales. Esto lo conseguimos mediante la orden round(a) Y aquí viene una parte muy importante —que estoy seguro se podría haber resuelto de otras maneras—: necesitamos que cada árbol tenga un aspecto diferente que el que está a su lado, por eso queremos introducir un desfase en la secuencia de los frames que nos muestra cada uno. Mi primera idea fué utilizar un sumatorio añadiendo un valor aleatorio (un random) a cada árbol. El problema que surgía es que para cada frame de la animación resultante la expresión iba variando según ese valor aleatorio, de modo que todos los árboles iban generando un “bailoteo” en los frames que mostraban que desbarataba todo el proceso. Tengo que volver a aclarar que no tengo ni idea de programación ni poseo la base técnica necesaria para lidiar con estas situaciones pero estoy seguro de que se tiene que poder resolver el problema usando número aleatorios. Pero no supe resolverlo de ese modo ni quise dedicarle más tiempo. Se me ocurrió, en cambio, una solución seguramente poco ortodoxa, pero que proporciona un resultado perfecto: al valor del ángulo que tiene un árbol “i” en un momento concreto le sumamos un número que siempre va a ser constante para ese árbol a lo largo de la animación, pero diferente al resto de los árboles. ¿Qué número?: una potencia del valor “i”, concretamente i elevado a 4 (pow(i,4)). Podríamos haber usado otra potencia cualquiera: 2, 3, 7, 29… no importa. La cuestión es que para cada árbol obtenemos un número único b que es igual a su valor angular entero sumado a un número constante a lo largo del tiempo pero único: b=round(a)+pow(i,4)
El problema es que el resultado de la anterior fórmula b=round(a)+pow(i,4) nos va a dar valores muy altos, que se van a salir fuera del rango 000º - 360º. Para solucionarlo recurrimos a las matemáticas más elementales: el ángulo 367º es exactamente el mismo que 7º (367º = 360º + 7º); 723º es lo mismo que 3º (723º = 360º + 360º + 3º); 6169º es igual a 49º (6169º = 360º*17º + 49º) ... Cualquier número que representa a un valor angular superior a 360º puede reducirse a su equivalente dentro de la circunferencia mediante un sencillo cálculo: — dividimos el número b para 360 — nos quedamos con su valor entero trunc — el valor obtenido lo multiplicamos por 360 — y el resultado se lo restamos a nuestro número inicial. Esa és la formula que tenemos aquí: b-((trunc(b/360))*360) Con el ejemplo 6169: 6169-((trunc(6169/360))*360) = 6169-((trunc(17,13611111111))*360 = 6169-(17*360) = 49 Y ya sólo nos queda asignar a cada frame el valor obtenido de la anterior fórmula, tanto en el color ($arbol.Diffuse_arbol360movie.Frame) como en el mapa de recorte ($arbol.Clip_arbol360movie.Frame).
No debemos olvidarnos de los planos que contienen el mapa de recorte que producirá la sombra proyectada por los árboles. Se supone que cada árbol debe ser (o por lo menos parecer) distinto, por lo tanto sus sombras deberían ser distintas también. Así que necesitamos aplicar un fotograma-alfa de nuestra textura animada en cada plano-sombra, diferente del que se aplique en los árboles de alrededor. A diferencia de los propios árboles, la sombra arrojada no debe cambiar en función del ángulo con que la miremos, es siempre la misma. Así pues definimos un nuevo valor c que es igual a cuarta la potencia del valor i (c=pow(i,4)) De este modo nos aseguramos que cada árbol tiene un valor único diferenciado del resto y constante a lo largo de toda la animación. Fijémonos que no irá cambiando porque no le sumamos el ángulo que toma ni el árbol ni el plano de sombra, eso es indiferente. Lo que sí necesitamos es acotar el valor resultante dentro del rango 000º - 360º, para que siempre pueda aplicarse uno de los 360 fotogramas de nuestra textura animada. Para ello volvemos a aplicar la misma fórmula que antes: c-((trunc(c/360))*360) Y el resultado es lo que se aplicará al mapa de recorte del plano-sombra: $sombra.Clip_arbol360movie.Frame
Conclusiones Todo esto es sólo el principio, está técnica se podría mejorar mucho, atacando con diferentes detalles: — Podríamos generar unos mapas de normales animados con nuestro árbol inicial 3D para añadir mucha más profundidad. — Deberíamos trabajar con más de un único árbol: quizá con 3 modelos de la misma especie, generando 3 peliculas de 360 frames que se irían distribuyendo aleatoriamente por todos los planos, para introducir mucha más variedad. — Podríamos generar diferentes resoluciones de una misma película VR (alta, a 1024px; media, a 512px; y baja, a 256px) para luego modificar el script de modo que asignara una u otra resolución dinámicamente, en función de la distancia de cada plano-árbol a la cámara. Y a continuación podemos apreciar el resultado que proporciona al movernos por entre los árboles (cosa que no podríamos hacer utilizando simples billboards). Tened en cuenta que no se trata de un proyecto cuidado: he ido muy deprisa para montar toda la escena, no he cuidado especialmente el texturizado del árbol original (algo que resulta básico para obtener un buen resultado), todos los planos tienen exactamente el mismo tamaño (algo que deberíamos ir modificando, para introducir más variedad). Por favor: consideradlo únicamente como un test para probar la técnica ;-) Además de la imagen fija podéis ver una pequeña animación de 4,5 MB.
NOTA 2: Si me quieres ayudar traduciendo los textos de este artículo al inglés, estaré encantado de crear una segunda versión adaptando todos los contenidos a ese idioma, incluyendo una nota de agradecimiento por la traducción y un link con tu web y/o email.
you are the visitor | eres el visitante |