Ejercicio 5: Cambios de camera


El ejercicio

El objetivo de este ejercicio es realizar cambios de camera en una escena. Específicamente, utilizando la escena del móvil se quiere mover entre una vista de primera persona, una de tercera persona y una vista amplia de toda la escena.

En este caso, la camera de primera persona va a ver el mundo desde el punto de vista de una de las esferas del móvil, la cámara rotara con la esfera. En tercera persona, la cámara va a ver la esfera desde el punto de vista de alguien que la este siguiendo de cerca por detrás, esta seguirá rotando junto a ella. Con la vista de la escena completa, la cámara vera al móvil desde la distancia y esta no rotara.

Debido a que mi móvil no presentaba todos los movimientos que debería, decidí basarme en el ejemplo del móvil de mi compañero Santiago Rojas para realizar este ejercicio. Decidí basarme en este ya que Santiago implementa un grafo de escena, lo cual facilita enormemente la implementación del cambio de escena.

Movimiento de Camara en WebGL

En WebGL, un objeto como tal no existe. WebGL muestra una visualización de los objetos que están presente en la escena como si la cámara estuviera ubicada en el origen (0, 0, 0) y mirando hacia el eje Z negativo. Por lo tanto, para dar la impresión de una cámara en distintas posiciones, debemos transformar todo el mundo alrededor de la cámara para que esta visualice lo que queremos. Es por esto que requerimos del uso de una matriz de vista, la cual, al ser multiplicada por la matriz de mundo de un objeto, nos de como resultado una nueva matriz de mundo donde el objeto este ubicado en relación a la cámara de forma tal que diera la sensación de que la cámara se acomodo al objeto.

En nuestro caso requerimos realizar los siguientes pasos:

  1. Calcular las matriz mundo de cada objeto. Es aqui donde el grafo de escena entra a jugar, calculando las matrices de mundo de los objetos según sus objetos padre.
  2. Calcular la matriz de cámara, según la posición y dirección de la escena donde queremos que nuestra cámara este.
  3. Calcular la matriz vista. La cual es la matriz inversa a la matriz de cámara, de esta forma estamos moviendo todo el mundo alrededor de la cámara y no la cámara alrededor del mundo.
  4. Calcular la matriz de proyección. Con esta definimos si el objeto es una cámara de perspectiva u ortogonal, además podemos ajustar el ángulo de visión de la misma y los planos cerca y lejos.
  5. Calcular la matriz de Vista-Proyección. La cual es la multiplicación de la matriz de proyección y vista.
  6. Multiplicar las matriz mundo de cada objeto por la matriz de proyección vista.
  7. Dibujar los objetos.

Podemos ver estos pasos en el codigo del ejercició:

    //1. Calcular las matrices de mundo
    levelA.updateWorldMatrix();

    //2. Calcular la matriz de camara.
    var cameraPosition = [0, -200, 0];
    var target = [0, 0, 0];
    var up = [0, 0, 1];
    cameraMatrix = m4.lookAt(cameraPosition, target, up);

    //3. Calcular la matriz de vista.
    var viewMatrix = m4.inverse(cameraMatrix);

    //4. Calcular matriz de proyección
    var aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
    var projectionMatrix = m4.perspective(fieldOfViewRadians, aspect, 1, 2000);

    //5. Calcular matriz de Vista-Proyección
    var viewProjectionMatrix = m4.multiply(projectionMatrix, viewMatrix);

    //6. Modificar las matrices de mundo.
    objects.forEach(
      object =>
        (object.drawInfo.uniforms.u_matrix = m4.multiply(
          viewProjectionMatrix,
          object.worldMatrix
        ))
    );

Desarrollo

Para realizar los cambios de cámara, creare tres botones en el HTML. Al ser presionados, cambiaran el valor de un elemento HTML escondido. WebGL podrá leer el contenido de este elemento y de esa forma saber que tipo de cámara debemos usar.

//--HTML--//
<div class="btn-group">
  <button onclick="getElementById('view').innerHTML = 'first'">Primera Persona</button>
  <button onclick="getElementById('view').innerHTML = 'third'">Tercera Persona</button>
  <button onclick="getElementById('view').innerHTML = 'long'">Escena Completa</button>
</div>
<h1 id="view" style="display: none;">long</h1>

//--JAVASCRIPT--//
var view = document.getElementById('view').innerHTML;

De los pasos mencionados anteriormente, solo debemos modificar el calculo de la matriz de cámara. Anteriormente, esto lo realizamos dándole un punto especifico a la posición de la cámara y por medio del método LookAt se calcula una matriz de forma que la cámara se ubique en esa posición y se fije en el punto indicado teniendo en cuenta el vector "arriba". Esta será nuestra forma de encontrar la cámara para ver la escena completa.

var cameraPosition = [0, -200, 0];
var target = [0, 0, 0];
var up = [0, 0, 1];
cameraMatrix = m4.lookAt(cameraPosition, target, up);

En las cámaras en primer y tercera persona sin embargo, la cámara no es estática, esta se mueve según se mueva el nodo en el que nos vamos a fijar. Podríamos obtener la posición de este nodo y su vector de movimiento y usar LookAt. Sin embargo, esta información ya se encuentra en la matriz mundo del objeto en cuestión. Por lo tanto, podemos decir que la matriz de cámara sea igual a la matriz de mundo de este objeto, de esta forma la cámara se ubicara exactamente en la posición del objeto y apuntara en la misma dirección que el objeto apunta. Para la cámara de primera persona solo necesitaríamos rotar la cámara para que esta apunte hacia la dirección del movimiento. Para la de tercera persona, la cámara tendrá que desplazarse cierta distancia del nodo, de modo que la cámara vera la parte trasera del nodo y lo seguirá según avance.

De esta forma, obtenemos nuestras tres cámaras:

  var view = document.getElementById('view').innerHTML;
  var cameraMatrix = m4.identity();
  // Cambiamos la camara segun la vista
  if (view == "first") {
    m4.copy(targetNode.worldMatrix, cameraMatrix);
    cameraMatrix = m4.translate(cameraMatrix, 0, 0, 0);
    cameraMatrix = m4.xRotate(cameraMatrix, degToRad(90));
    cameraMatrix = m4.yRotate(cameraMatrix, -degToRad(90));
  } else if (view == "third") {
    m4.copy(targetNode.worldMatrix, cameraMatrix);
    cameraMatrix = m4.translate(cameraMatrix, -50, 0, 10);
    cameraMatrix = m4.xRotate(cameraMatrix, degToRad(90));
    cameraMatrix = m4.yRotate(cameraMatrix, -degToRad(90));
  } else if (view == "long") {
    var cameraPosition = [0, -200, 0];
    var target = [0, 0, 0];
    var up = [0, 0, 1];
    cameraMatrix = m4.lookAt(cameraPosition, target, up);
  }

Cuando estamos en la cámara de primera persona, la cámara se encuentra dentro del nodo, por lo tanto este no se ve, solo se puede visualizar los otros objetos que están en el rango de visión del nodo. En tercera persona, si podemos ver el nodo y además un rango de visión mas amplio. Por ultimo, en la escena completa vemos todos los objetos y la cámara no realiza ningún movimiento.

Resultado final

El demo del ejercicio se puede ver en este fiddle.

Tiempo dedicado

Para este ejercicio, habia planeado un tiempo de 3 horas. De estas se usaron 2.5 horas gracias a las guias tomadas:

results matching ""

    No results matching ""