Explicación de un ejemplo:

Chill The Lion


El ejemplo

El ejemplo que estudiaremos es el de "Chill The Lion", una pequeña aplicación donde el usuario puede interactuar con un simpático león. Con el ratón, se controla el movimiento de un ventilador que el león curiosamente observa, al hacer clic, el ventilador se enciende y refresca al león. Esta pequeña escena es parte de los momentos creados por la compañía Epic, los cuales crean experiencias interactivas personalizadas.

Este ejemplo fue creado por Karim Maaloul, director creativo de Epic. Karim creo un pen con el código de Chill, el cual utilice para realizar esta explicación. En este enlace se puede ver la versión del código comentada por mi. El programa utiliza WebGL por medio de la librería Three.js. Sin embargo, para esta explicación, me centrare en mostrar como se pueden crear experiencias interactivas como esta con conocimientos básicos del API de WebGL. Por medio del uso de primitivas, eventos y transformaciones simples.

La geometría

Una de los aspectos más interesante de este ejemplo es su geometría. Tanto el león como el ventilador esta completamente creado a base de formas primitivas, principalmente cubos. Si revisamos con detalle el diseño del león, observamos que su cabeza es un cubo, los cuales tienen cubos de distintos colores incrustados en el para simular la nariz y los ojos. Orbitando la cámara alrededor, podemos ver que las patas y piernas del león también están hechas a bases de cubo. El cuerpo del león es inicializado como un cilindro, el cual tiene una de sus tapas deformada para dar la apariencia de un cuerpo triangular.

Este es un gran ejemplo para mostrar la capacidad de crear escenas interesantes con figuras sencillas. Three.js facilita la creación de estas primitivas, sin embargo, usando solo WebGL se podría recrear la misma escena sin requerir definir una cantidad exorbitante de vértices.

La interactividad

La interactividad de este ejemplo es sencilla pero efectiva. El programa esta escuchando por tres eventos distintos:

  • Mousemove: Movimiento del cursor en la aplicación.
  • Mousedown: El momento en que el usuario realiza un clic.
  • Mouseup: El momento en que el usuario deja de pulsar el clic.

Con estos eventos el programa realiza actualizaciones sencillas. Al detectar el movimiento del cursor, la aplicación actualiza una variable con la posición actual de este. Por medio de la detección de la presión y liberación del clic, la aplicación actualiza una variable que determina si el ventilador esta prendido o no. De esta forma, mientras el usuario mantenga el clic oprimido, el ventilador funcionara.

//Crewación de los event listeners
document.addEventListener('mousemove', handleMouseMove, false);
document.addEventListener('mousedown', handleMouseDown, false);
document.addEventListener('mouseup', handleMouseUp, false);

//Actualización de las variables
function handleMouseMove(event) {
  mousePos = {x:event.clientX, y:event.clientY};
}
function handleMouseDown(event) {
  isBlowing = true;
}
function handleMouseUp(event) {
  isBlowing = false;
}

También cabe mencionar que la aplicación escucha otros tres eventos: Touchstart, Touchend y Touchmove. Realizando actualizaciones similares. Por medio de estos eventos, la aplicación asegura el funcionamiento en dispositivos táctiles como teléfonos celulares.

Sin embargo, estos eventos solo actualizan las variables, no la geometría en si. Para esto, el programa crea un loop, una función que es ejecutada en cada fotograma de la aplicación. Esta función se encarga de llamar a la función que dibuja la escena, calcula la posición del cursor en referencia con el centro de la escena y llama a las funciones de actualización del león y el ventilador.

//Loop del programa-
function loop(){
  render();

  //Calcula la posicion del mouse en la escena.
  var xTarget = (mousePos.x-windowHalfX);
  var yTarget= (mousePos.y-windowHalfY);
  fan.isBlowing = isBlowing;

  //Actualiza el ventilar.
  fan.update(xTarget, yTarget);

  //Actualiza el leon segun el estado en que esta el ventilador.
  if(isBlowing) {
    lion.cool(xTarget, yTarget);
  }else{
    lion.look(xTarget, yTarget);
  }

  //Espera al siguiente fotograma para realizar el loop nuevamente.
  requestAnimationFrame(loop);
}

Examinando las funciones de actualización de la geometría, podemos ver como Karim creo la animación. Lo interesante de esto, es que se utilizan transformaciones sencillas de las partes del león. Sin embargo, el resultado es un movimiento muy fluido y natural.

El ventilador

Para la actualización del ventilador. Karim primero calcula la posición en la escena donde el ventilador debería estar según la posición del target encontrado en el loop. Esto lo realiza por medio de una función que define llamada rule3(x, Vmin, Vmax, Tmin, Tmax), lo que realiza esta función es trasladar un valor x dentro de un rango Vmin - Vmax a un valor proporcional en el rango Tmin - Tmax. Por medio de esta función logra realizar varios procedimientos útiles. Primero, logra limitar el movimiento de los objetos al rango que especifique, en el caso del ventilador entre -250 y 250. Por otro lado, invirtiendo el rango T logra invertir la posición, lo cual se requiere para transformar la posición Y del canvas a la de la escena de WebGL.

Para lograr un movimiento mas natural, Karim no mueve directamente el ventilador a la posición calculada. En cambio, mueve un fracción de la diferencia entre la posición actual y la posición final. Esto da como resultado un movimiento suave hacia la posición.

  // Calcula el posicion del mouse
  this.tPosX = rule3(xTarget, -200, 200, -250, 250);
  //Invierte Y
  this.tPosY = rule3(yTarget, -200, 200, 250, -250);

  // Hace un movimiento suave desde la posición actual hasta la del ventilador.
  this.threegroup.position.x += (this.tPosX - this.threegroup.position.x) /10;
  this.threegroup.position.y += (this.tPosY - this.threegroup.position.y) /10;

Además de la posición, se debe actualizar las aspas del ventilador. Para esto, el programa revisa si el ventilador esta encendido o no. Si lo esta, este aumenta la velocidad hasta llegar a una velocidad máxima. Si esta apagado, disminuye esta velocidad hasta llegar a cero. Posteriormente, rota las aspas del ventilador según el valor de esta velocidad.

  //Calcula la velocidad del ventilador
  this.targetSpeed = (this.isBlowing) ? .3 : .01;
  if (this.isBlowing && this.speed < .5){
    this.acc +=.001;
    this.speed += this.acc;
  }else if (!this.isBlowing){
    this.acc = 0;
    this.speed *= .98;
  }
  //Rota las aspas segun la velocidad.
  this.propeller.rotation.z += this.speed;

Un ultimo paso que realiza el ventilador es la operación LookAt. Esta es una operación que ofrece Three.js para lograr que un objeto este "mirando" a un punto especifico. Esto significa que la coordenada X positiva del objeto este apuntando hacia el punto deseado, rotando todo el objeto adecuadamente. En este caso, Karim utiliza la función para que el ventilador siempre este observando al león. Este efecto se podría conseguir en WebGL calculando el ángulo de rotación necesario en los tres ejes.

 //El ventilar siempre observa al leon
  this.threegroup.lookAt(new THREE.Vector3(0,80,60));

El león

Para la animación del león, se realizan tres operaciones: El movimiento del cuerpo, el movimiento de la cabeza y el movimiento de la melena. Sin embargo, estas operaciones cambian según el estado del ventilador. La idea es que si el ventilador esta apagado, el león parezca intrigado por el objeto, por lo tanto siempre lo esta mirando. Sin embargo cuando el ventilador se enciende, el león se refresca con el y su melena se empieza a mover.

Para el movimiento del cuerpo, las rodillas son rotadas de forma que se alejen del ventilador cuando este apagado o se acerquen si este esta encendido. La cabeza en cambio, es rotada para que este observando el ventilador cuando este apagado y rotada en el sentido opuesto cunado esta encendido. Karim logra fácilmente realizar estos cambios invirtiendo el rango en su función rule3.

//Cuando el ventilador esta apagado

//Calcula la rotación de la cabeza de forma que mire al mouse
  this.tHeagRotY = rule3(xTarget, -200, 200, -Math.PI/4, Math.PI/4);
  this.tHeadRotX = rule3(yTarget, -200,200, -Math.PI/4, Math.PI/4);

//Calcula la posicion de la cabeza para que se aleje del mouse
  this.tHeadPosX = rule3(xTarget, -200, 200, 70,-70);
  this.tHeadPosY = rule3(yTarget, -140, 260, 20, 100);
//Mantiene la misma profundidad
  this.tHeadPosZ = 0;
//Cuando el ventilador esta encendido

//Calcula la rotación de la cabeza de forma que el leon vea el lado contrario del mouse.
  this.tHeagRotY = rule3(xTarget, -200, 200, Math.PI/4, -Math.PI/4);
  this.tHeadRotX = rule3(yTarget, -200,200, Math.PI/4, -Math.PI/4);

//Calcula la posicion de la cabeza para acercarse al ventilador
  this.tHeadPosX = rule3(xTarget, -200, 200, -70,70);
  this.tHeadPosY = rule3(yTarget, -140, 260, 100, 20);
//Se acerca en profundidad
  this.tHeadPosZ = 100;

Karim crea una animación para la melena del león muy interesante. La geometría de esta es un grupo de cubos organizados a manera de grilla. Cuando el ventilador es encendido, estos aparentan moverse debido al viento emitido por el ventilador. Para esto, Karim mueve los cubos oscilantemente en el eje z, con distinta fase entre ellos. La frecuencia de oscilación aumenta si el ventilador se acerca al león, dando la apariencia que el viento esta soplando mas duro.

for (var i=0; i<this.maneParts.length; i++){
  var m = this.maneParts[i].mesh;
  var amp = this.maneParts[i].amp;
  var zOffset = this.maneParts[i].zOffset;
  var periodOffset = this.maneParts[i].periodOffset;

  m.position.z = zOffset + Math.cos(this.windTime+periodOffset)*amp*dt*2;   
}

Lo interesante de esta animación es que es relativamente sencilla, sin embargo le da un carácter y una personalidad al león, haciendo que la aplicación sea muy llamativa.

results matching ""

    No results matching ""