domingo, 21 de septiembre de 2014

"IK" no significa "¿Y qué?"

Qué encontrarás en esta entrada?
  • Cinemática inversa en Blender.
  • Imágenes y vídeos de ejemplo.


La cinemática inversa (en inglés, "inverse kinematics", conocida a veces como "IK") es una disciplina que intenta calcular el movimiento de una cadena de articulaciones dadas ciertas restricciones, por ejemplo, la posición de la punta de la cadena.

Esto puede tener aplicaciones interesantes en robótica. En animación 3D, también tiene su utilidad, que es de lo que vamos a hablar en esta entrada.

Hasta hace poco, para mi esta era una parte muy oscura de la animación. Mi intención es arrojar algo de luz sobre el procedimiento básico en Blender con los últimos conocimientos que he adquirido para todo aquel que se encuentre en la situación en la que yo estaba hace unos días.

Cuando queremos animar un modelo 3D, el primer paso es tener el modelo, por lo que, lógicamente, hay que modelarlo. No entraremos en este post en los detalles del mundo del modelado, sobre el cual hay información de sobra en la red.

Una vez tengamos nuestro modelo 3D inerte, le dotamos de un esqueleto (en Blender se le llama "armature"). Un esqueleto es una estructura formada por huesos ("bones"). La creación del esqueleto es un proceso muy parecido al del modelado, salvo que en vez de usar figuras geométricas, se utilizan huesos, que geométricamente no son más que objetos con dos vértices ("cabeza" y "cola"). Los huesos se construyen según cómo queramos que se mueva nuestro modelo. Entre hueso y hueso hay una articulación que puede rotar, deformando la parte del modelo a la que esté asociada.


Esqueleto y algunos movimientos posibles con cinemática inversa
Cuando tengamos construido nuestro esqueleto, hay que asociarlo con las partes del modelo que se van a mover. Esto se realiza seleccionando siempre primero el modelo, luego el esqueleto, y con "ctrl+p". Se selecciona entonces la opción de asignar pesos automáticos.


Con esto tenemos una asignación básica de la parte a la que afectaría cada hueso. En mi opinión, es muy importante revisarla. Desde el menú de edición del modelo, una vez asociado el esqueleto, en la pestaña de grupos de vértices, aparecerán grupos con los nombres que hayamos dado a cada hueso. Desde allí se puede revisar exactamente qué conjunto de vértices de nuestro modelo están afectados por cada hueso, y redefinirlo con la ayuda de los botones "assign" (asignar el conjunto de vértices seleccionado), "remove" (borrar el conjunto de vértices que había sido asignado previamente), "select" (seleccionar el conjunto de vértices que fue asignado previamente) y "deselect" (desmarcar el conjunto de vértices asignado).


Con esto habríamos acabado la tarea de añadir un esqueleto a nuestro modelo, y podríamos deformarlo moviendo cada uno de los huesos. Sin embargo, hay una forma más interesante de animar a nuestro personaje, y es con la cinemática inversa.

El primer paso es crear unos huesos auxiliares en el esqueleto que estén desemparentados. Este paso es importante, durante mucho tiempo mi error era pensar que estos huesos eran opcionales, y por ello no obtuve el resultado deseado. El objetivo de estos huesos auxiliares "libres" es que podamos "tirar de ellos" para que se arrastre la cadena que tienen enganchada. Se crea un hueso auxiliar en cada parte del esqueleto de la que queramos "tirar" y se desemparenta seleccionando la "x" del apartado "parent", de tal manera que éste quede vacío y no muestre el nombre de ningún otro hueso. Así le hacemos que sea libre, y podremos transladarlo a nuestro antojo.


Evidentemente, no nos interesa que sea libre del todo, porque si no, no afectaría a nuestro sujeto. Lo que tenemos que hacer es encadenarlo a la punta de la cadena. Para ello, seleccionamos el hueso del que vayamos a tirar, después el último hueso de la cadena, y pulsamos "shift+i", de esta manera definimos una relación "IK", que podremos utilizar más tarde. No es raro que se descoloque nuestro modelo en este paso. Eso se debe a que la cadena que tenemos definida es de nivel 0. Hay que aumentar el valor de la longitud de la cadena ("Chain Length") en tantas unidades como articulaciones queramos que sean afectadas.


Una vez hecho esto, en el modo de pose, moviendo cada hueso auxiliar la cadena que lleva enganchada se moverá con él, pudiendo manejar a nuestro modelo como un titiritero tirando de cada cuerda. No tocamos todas las articulaciones, sino que tiramos de las puntas de las cadenas, y la cinemática inversa se encargará de recolocar el resto de los huesos cumpliendo con las restricciones que le hayamos metido.

Una vez obtenido el resultado deseado, lo guardamos con "i" y seleccionamos "Whole Character". Nos movemos a cualquier otro fotograma y repetimos la operación. Blender se encargará de generar una transición continua que una las dos poses.

A continuación, un muy breve ejemplo de lo que me ha salido a mi.


Estoy trabajando en un proyecto más amplio donde podréis ver a este simpático dinosaurio en acción. Espero poder enseñároslo pronto y también que este breve tutorial os haya aclarado algunas de vuestras dudas.

domingo, 14 de septiembre de 2014

Capri, ¡porque yo lo valgo!

Qué encontrarás en esta entrada?
  • Uso de partículas tipo "hair" (pelo) en Blender.
  • Animaciones de muestra.

No es la primera vez, y espero que tampoco la última, que hablamos en esta página de Blender, un fabuloso programa de diseño 3D multiplataforma y 100% abierto.

En las últimas versiones de este software hemos podido ver cómo se le ha dado más importancia a las características relacionadas con la física ("physics") y con las denominadas "partículas" ("particles"). Entre éstas últimas, hay unas de tipo "hair" (pelo) que presentan interesantes posibilidades.

Esta entrada no pretende ser un tutorial paso a paso sobre la creación de pelo, para ello os recomiendo que os empapéis de los múltiples videotutoriales que hay en YouTube al respecto.

Para crear un objeto peludo en la versión 2.71, tan sólo hay que acudir a la pestaña "particles" y añadir un nuevo conjunto de ellas. En "type" habrá que seleccionar el valor "hair" (pelo) y ya podremos ver cómo a nuestro objeto le crecen unos pelos perpendiculares bastante feos. Cambiando el número de ellos y su tamaño podemos empezar a afinar el resultado. Respecto al número, comentar que Blender trabaja con "padres" ("parents") e "hijos" ("childrens"): los padres marcan la guía principal que luego seguirán los hijos, por lo que si queremos 100 pelos, no hace falta que los 100 sean "padres" (todos "pelos-guía"), a lo mejor basta con utilizar 10 "padres", y 10 "hijos" por cada padre que les sigan. Esta forma de proceder puede aliviar el trabajo en la aplicación, puesto que mientras que estamos con ella podremos elegir se verlos todos, sólo los padres, o cierto porcentaje intermedio de los hijos.


Respecto a la dinámica, configurable desde el módulo "hair dynamics" (dentro de la misma pestaña "particles"), os recomiendo que veáis éste vídeo.

Con todo ello, ya podemos hacer nuestras primeras pruebas. Lo primero que se me ocurrió hacer, fue hierba mecida por el aire.


Y lo primero que descubrí, fue la gran pega de este proceso: aunque el resultado puede ser muy impresionante sin demasiado esfuerzo (ya que el pelo, o en este caso la hierba, se genera por sí sola siguiendo unos cuantos parámetros), el tiempo del renderizado se dispara, poniendo a prueba las características de tu equipo. En mi caso, he llegado a poner mis ocho procesadores al 100% y la CPU a 80ºC, necesitando colocar un ventilador grande frente al ordenador para mejorar la disipación de calor y trabajar con unas temperaturas más razonables.

Monitorización del sistema vía móvil durante el proceso de renderizado del vídeo anterior

En aquel caso eran medio millón de hierbas (500 padres x 1.000 hijos = 500.500) moviéndose a la vez y tardó en renderizar unas 8 horas.

El siguiente ejemplo que se me ocurrió fue hacer una bestia salvaje con su pelaje... aunque terminó desembocando en una especie de fauno atontado llamado "Capri". Véase a continuación su hipnótico movimiento de pelambrera.


En este caso hay varios cambios con respecto a lo anterior. En primer lugar, el objeto emisor de pelo se mueve, y se comprueba cómo al añadirle dinámica a las partículas, el pelo que brota de él adquiere una dinámica consecuente de manera automática. Tuve algunos problemas al intentar dotar al objeto de esqueleto, puesto que éste puede deformarlo, y comprobé que - al menos de forma automática -  el pelo que brota de un objeto no se adapta a la deformación del mismo: sólo a las rotaciones y translaciones rígidas de la pieza en conjunto.

Otro cambio con respecto al primer intento es que descubrí que puede haber varios sistemas de partículas asociadas al mismo objeto, incluso podemos elegir qué grupo de vértices se ven afectados por cada conjunto de partículas. Esto nos permite, por ejemplo, que la cabeza tenga un pelaje corto uniforme, pero que la parte asociada al crecimiento de la barba, además, tenga otro grupo de partículas con unas características particulares. En referencia a esto, os recomiendo el vídeo de este enlace.

Hagamos un recuento del pelo para Capri:

  • Cabeza: 1.000 padres x 300 hijos (301.000).
  • Oreja grande: 500 padres x 100 hijos (50.500).
  • Oreja pequeña: 500 padres x 50 hijos (25.500).
  • Barba: 100 padres x 10 hijos (1.100, con dinámica).
  • Párpados: 2 x 500 padres x 50 hijos (51.000).
  • Cuerpo: 1.000 padres x 300 hijos (301.000).

En total, Capri tiene 730.100 pelos, 1.100 de ellos en movimiento. No es de extrañar que tardase nada más ni nada menos que 12 horas y media horas en renderizar una secuencia de 10 segundos.

Monitorización de Blender a través de Hangouts (ver entrada "Render in Progess")

Por último, quería comentar la influencia del "anti-aliasing" en el tema del cabello en Blender. El "anti-aliasing", además de las consideraciones sobre las limitaciones de la representación digital de una señal analógica de cierta frecuencia, dada una frecuencia de muestreo, para lo que nos ocupa ahora es una especie de suavizado para que la transición entre píxeles no sea tan abrupta. En el caso del cabello, al estar hablando de finas hebras de material sobre un fondo dado, el suavizado de bordes puede tener cierta importancia sobre la textura que aparentan tener éstas.


En la imagen de arriba se puede ver que no hay mucha diferencia entre el anti-aliasing de 8 y el de 16 (la imagen ha sido escalada, pero en las imágenes originales la diferencia también era casi inapreciable). Sin embargo, la ausencia total de anti-aliasing produce una textura mucho más áspera para el caso que nos ocupa, incluso le da una tonalidad más oscura cuando se ve de lejos. El aumento del anti-aliasing hace aumentar a su vez los tiempos de renderización. Es por tanto necesario encontrar un equilibrio entre ambas consideraciones en función de nuestras necesidades.

domingo, 7 de septiembre de 2014

Render in progress

Qué encontrarás en esta entrada?
  • Monitorización renderizaciones en Blender en Linux.

En Astaroth's World no es la primera vez que intentamos monitorizar Blender. El proceso de renderizado (creación de una imagen digital a partir de un diseño previo), es un proceso en la mayor parte de las ocasiones costoso tanto a nivel de recursos del sistema como en tiempo. No es raro que un proyecto de animación amateur tarde días o semanas en renderizar, y este tiempo siempre puede dispararse hasta el infinito según las calidades que queramos o la duración de nuestra secuencia. Por ello, es especialmente interesante poder monitorizar el proceso, incluso a distancia, mientras que invertimos ese tiempo en otras actividades.

La solución propuesta hace meses en este blog involucraba un costoso análisis del vídeo generado. Como el vídeo en formación estaba incompleto, primero lo reconstruía corrigiendo el índice, y luego contaba los fotogramas y el tiempo (relacionados por los "fotogramas por segundo"). Finalmente, lo enviaba por correo usando mutt. De esta manera podíamos programar un informe periódico para recibirlo en el correo electrónico (en aquella versión incluso se incluía el último fotograma renderizado extraído del vídeo en formación).

La solución propuesta ahora es más eficiente, puesto que aprovecha los propios recursos de Blender y no implica un procesamiento de vídeo mientras que se está generando (muy costoso para el sistema).

La clave está en que Blender permite un acceso por línea de comandos (más información aquí), de tal manera que se puede renderizar en una consola de Linux sin necesidad de abrir el entorno gráfico de la aplicación. A priori, esto parece ser más eficiente para el sistema. A cambio, perdemos la capacidad de ver el fotograma que se está generando, lo cual a veces puede resultar útil para detectar errores durante el renderizado.

La sintaxtis básica podría ser la siguiente:

./blender -b archivo.blend -s 001 -e 100 -a -x 1

De esta manera se renderizaría el proyecto guardado en "archivo.blend" desde el primer fotograma hasta el número 100. En pantalla podremos ver algo parecido a lo siguiente:

Fra:1 Mem:23.51M (0.00M, Peak 29.46M) | Preparing Scene data
Fra:1 Mem:23.51M (0.00M, Peak 29.46M) | Preparing Scene data
Fra:1 Mem:23.51M (0.00M, Peak 29.46M) | Creating Shadowbuffers
Fra:1 Mem:23.51M (0.00M, Peak 29.46M) | Raytree.. preparing
Fra:1 Mem:34.57M (0.00M, Peak 34.57M) | Raytree.. building
Fra:1 Mem:33.92M (0.00M, Peak 51.19M) | Raytree finished
Fra:1 Mem:33.92M (0.00M, Peak 51.19M) | Creating Environment maps
Fra:1 Mem:33.92M (0.00M, Peak 51.19M) | Caching Point Densities
Fra:1 Mem:33.92M (0.00M, Peak 51.19M) Sce: Scene Ve:180482 Fa:80512 La:1
Fra:1 Mem:33.92M (0.00M, Peak 51.19M) | Loading voxel datasets
Fra:1 Mem:33.92M (0.00M, Peak 51.19M) Sce: Scene Ve:180482 Fa:80512 La:1
Fra:1 Mem:33.93M (0.00M, Peak 51.19M) Sce: Scene Ve:180482 Fa:80512 La:1
Fra:1 Mem:33.93M (0.00M, Peak 51.19M) | Volume preprocessing
Fra:1 Mem:33.93M (0.00M, Peak 51.19M) Sce: Scene Ve:180482 Fa:80512 La:1
Fra:1 Mem:33.93M (0.00M, Peak 51.19M) Sce: Scene Ve:180482 Fa:80512 La:1
Fra:1 Mem:48.35M (0.00M, Peak 51.19M) | Scene, Part 8-135

Como vemos, en este "log" se da información a tiempo real sobre el proceso. Lo interesante es que no es difícil redirigir esta salida en pantalla a un archivo de texto, basta con hacer:

./blender -b archivo.blend -s 001 -e 100 -a -x 1 > log.txt

Con ello conseguimos obtener un archivo "log.txt" en el que venga información a tiempo real sobre el renderizado. Bastaría con leerlo para saber en qué punto se encontraría nuestra animación.

El flujo de trabajo constaría entonces de dos partes: renderizado por consola y lectura de los datos generados. Para la primera parte podríamos utilizar un script parecido al siguiente:

#!/bin/bash
tmps=`date +%s`
echo "$tmps;$2;$3" > log.txt
dir=`pwd`
cd /url_donde_este_blender/
ini=`printf "%03d" $2`
fin=`printf "%03d" $3`
./blender -b $dir/$1 -s $ini -e $fin -a -x 1 >> log.txt
Si a este archivo le llamásemos "render.sh", habría que ejecutarlo en la misma carpeta donde estuviera el ".blend" de la siguiente manera:

./render.sh archivo.blend 1 100

En dicho caso, cogería el archivo "archivo.blend" y renderizaría la animación desde el fotograma 1 al 100. Primero tomaría el tiempo inicial (para poder dar estimaciones de cuánto va a tardar). Luego, escribiría esa información, junto con la de los fotogramas iniciales y finales en el log (para poderlos usar más tarde en nuestros cálculos posteriores). Tras dar el formato adecuado a los números de fotogramas renderizaría la animación mandando el log a un archivo de texto.

Ya se está generando la animación, y nuestro log tiene la siguiente pinta:

1410073356;1;100
Read new prefs: /********/userpref.blend
found bundled python: /********/python
read blend: /********/untitled.blend
Fra:1 Mem:23.51M (0.00M, Peak 29.46M) | Preparing Scene data

En la primera fila, separados por ";", tenemos los datos sobre tiempo y fotogramas que hemos introducido nosotros a través del primer script. La última fila con el texto "Fra:[número]" nos da el último fotograma con el que se está trabajando. Con todo ello, podemos iniciar la segunda fase: lectura de los datos generados.

   Un script como el siguiente leería los datos del log y nos calcularía lo que deseamos:

# Tiempo de inicio de la animación:
tinibl=`cat log.txt | sed -n '1p' | awk -F\; '{print $1}'`

# Primer fotograma:
fotblendin=`cat log.txt | sed -n '1p' | awk -F\; '{print $2}'`

# Último fotograma:
fotblendfn=`cat log.txt | sed -n '1p' | awk -F\; '{print $3}'`

# Archivo blender renderizado:
archblen=`cat log.txt | sed -n '4p' | grep -o "/.*blend"`

# Último fotograma renderizado:
framblen=`cat log.txt | grep -o "Fra:[0-9]*" | sed -n '$p' | grep -o "[0-9]*"`

# Hora actual:
tfinbl=`date +%s`

# Tiempo de renderizado hasta el momento:
tbl=`echo "scale=2; ( $tfinbl - $tinibl ) / 60" | bc`

# Fotogramas por renderizar (el último de la animación menos el último renderizado):
fotporren=`echo "$fotblendfn - $framblen" | bc`

# Fotogramas renderizados (el último renderiado menos el primero de la animación):
fotyarend=`echo "$framblen - $fotblendin" | bc`

# Porcentaje renderizado:
porcblen=`echo "( $fotyarend * 100 ) / ($fotblendfn - $fotblendin)" | bc`

# Estimación de lo que queda por renderizar:
tiempestbl=`echo "scale=2; $fotporren * $tbl / $fotyarend" | bc`

# Redondeo del tiempo:
tiemptrunc=`echo "$tiempestbl / 1" | bc`            # truncado.
tdiscb=`echo "($tiempestbl * 10 - $tiemptrunc * 10) / 1 " | bc`    # Regla redondeo.
if [ $tdiscb -lt 5 ] ; then
    inctimbl=$tiemptrunc
else
    inctimbl=`echo "$tiemptrunc + 1" | bc`
fi

# Fecha fin:
ffinble=`date +%d/%m/%Y\ \(%H:%M\) -d "+$inctimbl min"`

Y con los datos ya leídos, simplemente tenemos que redirigirlos hacia donde queramos. Por ejemplo, podríamos mandarnos un correo electrónico con ellos a través de mutt. Mi opción ha sido aprovechar el sistema que me estoy creando basado en centerIM y Hangouts para poderme comunicar con mi ordenador a través de mi móvil (ver más acerca de esto). De esta manera, puedo preguntar por el móvil cómo va la renderización y recibiría algo como lo siguiente.


En fin, una herramienta más para sobrellevar el tedioso tiempo de espera durante el renderizado.