Macros de transformación aplicables a animaciones

POV-Ray incluye en su librería, macros de gran utilidad para hacer transformaciones, por lo que recomiendo revisar los archivos de inclusión y su tutorial sobre ellos antes, para ver si ya existe lo que uno está por escribir o resolver. Por otra parte algunos usuarios van creando otras en la medida en que las necesitan y las comparten en la Web, por lo que también es útil revisar en ella. En el archivo de inclusión "transforms.inc", por ejemplo, encontraremos algunas macros interesantes:

 // Centra un objeto con respecto a un eje
     Center_Trans(Object, Axis)  
 // Alinea un objeto con repecto a un eje
     Align_Trans(Object, Axis, Pt) 
 // Translada un objeto a lo largo de una spline.
     Spline_Trans (Spline, Time, Sky, Foresight, Banking) 
 // Aplica una transformacion específica a un vector
     vtransform(vec, trans)
 // Aplica una transformacion inversa a un vector
     vinv_transform(vec, trans)
 // Rota un objeto alrededor de un punto con un angulo específico.
     Rotate_Around_Trans(Rotation, Point) 

Rotate_Around_Trans macro, aplicada objetos y vectores

Esta macro funciona al modo de Center en el campo Rotation en VRML. Basicamente traslada nuestro objeto hasta el origen de coordenadas, realiza la rotación y lo devuelve a su posición original. Para el caso del muñeco o maniquí articulado, esta macro nos permite realizar lo que se conoce como "Cinemática Directa" (Forward Kinematic) aplicando ángulos a sus puntos de flexión. Así, suponiendo que el esqueleto del muñeco estuviera compuesto por articulaciones en la cintura, en la base del tórax hacia su espalda, en la base del cuello y en la nuca, para lograr que la cabeza tomara su posición correspondiente, habría que aplicar las rotaciones correspondientes a cada articulación de la siguiente manera:

 #declare escala=transform{scale 2.54}
 object{Cabeza
  Rotate_Around_Trans(Rot_Nuca, PtoNuca) 
  Rotate_Around_Trans(Rot_Cuello, PtoCuello) 
  Rotate_Around_Trans(Rot_Torax, PtoTorax) 
  Rotate_Around_Trans(Rot_Cintura, PtoCintura)
  transform{escala}
 } 

Hasta aquí se resuelven las cosas de manera bastante simple. Incluí al final del código un escalado de pulgadas a cm, para ver como podemos trabajar con distintas transformaciones, por ejemplo con objetos diseñados en otra escala de unidades, es decir, en donde el espacio local del mismo no coincide con el global. Sin la transformación, un modelo diseñado en pulgadas se vería fuera de escala en un entorno calculado en centímetros. Por ejemplo, un modelo de 69 pulgadas o unidades, se vería como de 69 cm en lugar de 175.

Supongamos que queremos empezar a darle una mayor expresión a nuestro muñeco haciendo que gire sus ojos hacia determinada dirección en el espacio. Existen varias soluciones para esto. Podemos transformar las coordenadas del punto en el espacio global a las coordenadas locales del muñeco, efectuar las rotaciones correspondientes, para que luego la cabeza del mismo se incorpore con los ojos ya rotados. También podemos hacer lo contrario, es decir, definir la cabeza sin los ojos, y agregarlos ya rotados al final (una solución que podría en algunos casos ser mas directa y simple). El incluir los ojos ya rotados en la cabeza es una opción mas complicada que requiere de cálculos previos que tengan en cuenta la pose, pero estos son necesarios cuando se quiere aplicar por ejemplo cinemática inversa (IK ó Inverse Kinematic), para que al mirar rote también cabeza y cuello dándole naturalidad al movimiento de enfoque visual. Esta opción es interesante, ya que nuestro muñeco empezaría a tener cierta autonomía de movimientos, pero algo complicada de explicar en esta instancia, por lo que la dejaré para la página de Macros de animación automática e interactiva. En cualquiera de los casos, seguramente vamos a necesitar las coordenadas en donde está localizado el ojo y aplicarle las rotaciones correspondientes al cuerpo y la del ojo en cuestión. Para rotar un vector en lugar de un objeto habremos de utilizar juntas las macros vtransform y Rotate_Around_Trans, o escribir una nueva macro, que involucre a las dos.

 #macro vRotar(PuntoaRotar, PuntodeRotacion, AngulodeRotacion)
  #local fn = function { 
      transform {translate -PuntodeRotacion
                 rotate AngulodeRotacion
                 translate PuntodeRotacion
                }
               }
  #local result = (fn(PuntoaRotar.x, PuntoaRotar.y, PuntoaRotar.z));
   result
 #end

 #declare OjoDr_rp = vRotar(OjoDr_rp, PtoNuca, Rot_Nuca);
 #declare OjoDr_rp = vRotar(OjoDr_rp, PtoCuello, Rot_Cuello );
 #declare OjoDr_rp = vRotar(OjoDr_rp, PtoTorax, Rot_Torax);
 #declare OjoDr_rp = vRotar(OjoDr_rp, PtoCintura, Rot_Cintura);
 #declare OjoDr_rp = vtransform(OjoDr_rp, escala);

Eye - Looking Adjust

El núcleo de la función es la transformación Rotate_Around_Trans propiamente dicha. La técnica de declararla como una función que nos devuelva un resultado es la utilizada por la macro vtransform. Haciendo una adaptación obtendríamos por ejemplo la ubicación del centro del ojo derecho de la figura en cualquier pose, o de cualquier objeto "asociado" a ella, por ejemplo unos lentes. La macro VPos(Punto, Pos) suministrada en el archivo descargable que aparece mas adelante en esta página, realiza la transformacion de cualquier punto con respecto a cualquier objeto de la figura.

Eye - Looking Adjust

Si calculamos anticipadamente los ángulos correspondientes a la rotación del ojo y efectuamos un Rotate_Around_Trans sobre sus ejes, lo colocaremos en la forma adecuada desde un principio. También podemos agregarlo al final utilizando la macro Orient de Spiritone, que se basa en la función vcross. Esta función devuelve el producto escalar (producto cruz) entre dos vectores. Hay un buen ejemplo de su uso en su Cyclopedia, en donde muestra como utilizarla para orientar una imagen de fondo hacia la cámara, de modo similar al nodo Billboard en VRML. La macro coloca un objeto que mira hacia +z en la posición p1 y lo orienta hacia el vector p2. Así, podemos utilizarla para orientar texto o cualquier otro objeto hacia nuestro punto de vista o hacia cualquier otro que se nos ocurra. La página que dedica a vcross explica como utiliza la función para obtener el vector perpendicular y reorientarlo mediante una matriz de transformación.

// Coloca un objeto en la posicion V_Locacion y lo orienta hacia el vector V_Direccion. 
// Uso:
// object{MiObjeto OrientZ(V_Locacion, V_Direccion) 
 #macro OrientZ(p1,p2)
  #local v_Sky=<0,1,0>;
//  #local nz = vnormalize(p2-p1); // Objeto frente a +z 
  #local nz = vnormalize(p1-p2); // Objeto frente a -z 
  #local nx = vnormalize(vcross(v_Sky,nz)); 
  #local ny = vcross(nz,nx);
   matrix < nx.x,nx.y,nx.z, ny.x,ny.y,ny.z, nz.x,nz.y,nz.z, p1.x,p1.y,p1.z >          
 #end

Existen muchos tutoriales que explican como se realiza traslacion, escalado y rotacion mediante matrices. POV-Ray incluye la instrucción matrix para realizar transformaciones mediante matrices. Algunos tutoriales que explican como utilizar esta instrucción son:
Understanding the POV-Ray matrix keyword por Jeff Lee
The Matrix Page por John
Geometric Transformations with POV-Ray - matrix por Friedrich Lohmueller

En el caso de que nuestra figura mire hacia -z, habremos de girarla 180 grados o podemos cambiar la línea con la definición de nz por esta otra:

 #local nz = vnormalize(p1-p2); 

En el caso del Ojo, el código para colocarlo en su posición, mirando por ejemplo hacia la cámara, sería:

 object{Ojo OrientZ(OjoDr_rp, Camara_Locacion)}
Figure Macros - Template Reuní las macros de rotación que utilizo, en un archivo que puedes descargar desde aquí, junto con una plantilla para armar una figura articulable y hacer tus propios ensayos.

Otras macros de utilidad

Las macros nos permiten escribir archivos de código mas limpios y menos extensos si las incluimos en un archivo creado para ese fin, al que podamos recurrir siempre que lo necesitemos con una simple línea #include Esto nos evita también el escribir, recordar y repetir constantemente, muchas líneas de código a veces extensas o complejas, invocándolas con una simple instrucción. Las siguientes son pequeñas macros que escribí con este objetivo y también incluyo algunas de otros autores. Reitero mi recomendación inicial de revisar la Web para descargar las macros y archivos de inclusión que comparten otros usuarios. Se puede acceder sus sitios a través de la página de enlaces o utilizar buscadores como el Google cuando se requiera algo específico.

Esta pequeña macro sirve para convertir al sistema de coordenadas derecho o izquierdo, de acuerdo a las necesidades del caso. Se puede aplicar, por ejemplo a un objeto diseñado con el Programa Moray (Right Coordinate System) que se quiera incluir en un archivo de sistema "izquierdo" o para incluir un objeto "izquierdo" (Left Coordinate System) en el código de un archivo generado por Moray.

// Convert Right Coord. System <===> Left Coord. System
 #macro CCS()
   transform{scale <1,1,-1> rotate x*90 }
 #end

Dos macros de interpolacion:

// Interpola la transicion desde un vector a otro.
 #macro TransV(V1, V2, reloj)
   V1 + reloj * (V2-V1)
 #end  

// Interpola desde una textura a otra.
// Uso: object{ MiObjeto TransTexture(Textura1,Textura2, MiReloj)}
 #macro TransTexture(Texture1,Texture2, reloj)
   texture{average
     texture_map {
        [1-reloj  Texture1]     
        [reloj    Texture2]     
      }
   }
 #end

Macros de segmentos de tiempo regulada por valores de reloj o por numero de frame. En la primera, mientras la variable clock sea menor a I_ClockSgmt nos devolverá el valor cero. A valores mayores nos devolverá 1. Estos dos valores se pueden modificar a voluntad, de acuerdo a las necesidades. En los valores intermedios nos devolverá valores desde 0 a 1. La segunda macro funciona haciendo uso de la primera. Para ello hay que definir el primer cuadro del segmento, el ultimo cuadro del segmento y la cantidad total de cuadros de la animación.

// Macro SgmtClock(ValorInicialReloj, ValorFinalReloj)
// Usos: 
// #declare MiReloj = SgmtClock(0.3, 0.7);
// object{ MiObjeto translate x * SgmtClock(0.3, 0.7) }
 #macro SgmtClock(I_ClockSgmt,F_ClockSgmt)
   #switch(clock)
     #range(0,I_ClockSgmt)
      #local Valor=0;  
     #break
     #range(I_ClockSgmt,F_ClockSgmt)
      #local Valor= (clock-I_ClockSgmt) / (F_ClockSgmt-I_ClockSgmt);
     #break
     #range(F_ClockSgmt,1)
      #local Valor=1; 
     #break 
   #end
   Valor
 #end

// Macro SgmtFrames(CuadroInicial, CuadroFinal, TotaldeCuadros)
// Uso: 
// #declare MiReloj = SgmtFrames(27, 63, 90);
 #macro SgmtFrames(I_Frame, F_Frame, TotalFrames)
    SgmtClock((I_Frame-1)/(TotalFrames-1),(F_Frame-1)/(TotalFrames-1))
 #end

// Macro SgmtTime(TiempoInicial, TiempoFinal, TiempoTotal)
// #declare MiReloj = SgmtTime(27, 63, 100);
 #macro SgmtTime(I_Time, F_Time, TotalTime)
    SgmtClock( I_Time / TotalTime , F_Time / TotalTime )
 #end

Una macro de Margus Ramst. Retorna un punto sobre una linea definida por dos vectores P1 y P2, a una distancia recta especifica desde P2. El punto puede ser sobre esa línea hacia P1 o en sentido contrario, si la distancia dada es negativa o positiva. La distancia entre los dos puntos puede averiguarse mediante vlength(P2-P1).

// By: Margus Ramst  
// P1 - start point , P2 - end point, Dist - distance from P2
 #macro v_lineup(P1,P2,Dist)
     (P2+vnormalize(P2-P1)*Dist)
 #end


Copyright 2006 Rodolfo Nothstein
Created: Nov 17 2006 - Last Change: Mar 31 2007