UNITY Cámara en perspectiva

De MediaWiki
Ir a la navegación Ir a la búsqueda

Introducción

  • La cámara va a ser nuestros ojos. Van a mostrar la escena como si estuviéramos nosotros viendo con nuestros ojos los objetos 3D dibujados a nuestro alrededor.
  • En el desarrollo de videojuegos podemos hacer uso dos tipos de cámara diferentes:
  • Cámara ortográfica: Utilizada en los juegos 2D.
  • Cámara en perspectiva. Utilizada en los juegos 3D.
  • En este punto vamos a tratar solo la cámara en perspectiva.
  • Preparación:
  • Crea una nueva escena de nombre Escena_UD2_Camara_Perspectiva




Cámara en perspectiva

  • Este tipo de cámara va a 'dibujar' los objetos en perspectiva, en función de la distancia, de tal forma que los objetos más cercanos se verán más grandes y los objetos más lejanos se verán más pequeños.


LIBGDX UD4 Camara 10.jpg
En este gráfico, la cámara estaría situada en en cuadrado más pequeño, mirando cara a los triángulos. Después explicaremos más en detalle.
Como vemos existen dos triángulo, el triángulo rojo está delante del verde, por lo tanto debe de verse mayor (los dos tienen el mismo tamaño).
Veamos el resultado:
LIBGDX UD4 Camara 12.jpg


  • Veamos un poco más en detalle los parámetros que conforman una cámara en perspectiva.

Parámetros de una cámara en perspectiva

LIBGDX UD4 Camara 11.jpg


En la imagen anterior el ojo representa a una cámara.
Como vemos, de él 'surgen' rayos formando un pirámide que se va abriendo hasta el infinito.
  • Cuando configuramos una cámara en perspectiva podemos determinar desde que punto y hasta que punto se visualizan los objetos.
Fuera de estas dos 'distancias' los objetos no se dibujarán.
Esos dos puntos en la figura que representa el área de visualización (como una pirámide aumentando de tamaño), forman dos planos que es lo que se conoce como el plano near (el situado en el punto más cercano) y el plano far (el situado en el punto más alejado)
El área de visualización (lo que va a dibujar la cámara) se conoce como view frustum.
Normalmente cuando definimos una cámara 3D indicamos un punto muy cercano (por ejemplo, 0.3) y un punto muy lejano (1000) aunque este último va a depender del tipo de juego. Recordar que estamos hablando de 'unidades' en el mundo 3D que son los cuadrados que se ven en el suelo.
¿ Por qué limitar el área de visualización ? Para ahorrar recursos. Ten en cuenta que un objeto 3D muy alejado no será apreciado por la vista, pero el ordenador seguirá dibujándolo aunque no lo veas.


  • Existe otro parámetro que es el Field of View => FOV
LIBGDX UD4 Camara 14.jpg
Este parámetro es un ángulo y representa el ángulo de visión de la cámara sobre la proyección de los objetos.
Viene a ser como la apertura del objetivo de una cámara. Cuanto más grande, más abarcamos.
El valor por defecto es de 60 grados, pero dependiendo del juego puede ser que nos interese aumentarlo, como en los juegos de primera persona.
Cabe señalar que al disminuir el ángulo conseguimos un 'efecto zoom' como se ve en la imagen anterior.
IMPORTANTE: En la cámara en perspectiva tiene que cumplirse que 0 < near < far.


  • Por último tenemos el viewport.
En una cámara 3D, el viewport va a establecer la relación de aspecto (la relación que existe entre el ancho y el alto de la pantalla). Hablaremos de esto en el siguiente punto.


  • En Unity3D podemos seleccionar la cámara en le ventana de jerarquía y podremos observar los valores de todos estos parámetros en la ventana inspector:
Unity3d camara 1.jpg
Podemos observar:
  • FOV tiene un valor de 60 grados.
  • El plano near está situado a 0.3 unidades de la cámara y el plano far a 1.000 unidades de la cámara. Por lo tanto la cámara va a 'dibujar' los objetos situados entre 0.3 y 1000 (es el view frustum)
  • La relación de aspecto va a ser 1 a 1 (valor del viewport).



  • Si seleccionamos la cámara en la ventana de jerarquía podemos ver en la ventana de escena el área de visualización de la cámara que viene representado por unas líneas blancas que surgen de ella formando una pirámide:
Unity3d camara 2.jpg




Relación de aspecto

  • Todos estamos 'cansados' de escuchar algo como: Me voy a comprar una televisión 16:9, o voy a comprarme un monitor 16:10
Estos números representan la relación que existe entre el ancho (primer número) y el alto (segundo número)
Unity3d cam rel aspecto 1.JPG


  • Cuando alguien filmar una película lo hacemos utilizando la cámara con una relación de aspecto determinada.
¿ Qué pasa cuando intentamos visualizar lo filmado en una televisión con una relación de aspecto diferente a la original ?


Unity3d cam rel aspecto 2.JPG
Imagen obtenido de esta web
Que aparecen las temidas bandas negras :)
Esto es así para que la película no se deforme:
Unity3d cam rel aspecto 3.JPG
En esta imagen, el ancho de ha duplicado, pero la altura no, por lo que la imagen aparece 'deformada'.


  • En juegos pasa algo parecido, con la ventaja que los objetos son dibujados en el momento que se juega (no está filmado previamente, bueno, aunque puede ser que haya escenas animadas previamente hechas).


  • Lo que tenemos que tener claro es que la cámara tiene una relación de aspecto que se lo da el ViewPort en su valores W y H:
Esa relación de aspecto no tiene que ver con el dispositivo físico donde se va a ejecutar el juego.
Así, si ejecuto el juego en una pantalla con 16:9, en principio se vería deformado.
Para evitarlo, Unity (si no tocamos los valores por defecto) ajustará, al iniciar el juego, el ViewPort al tamaño de la pantalla para mantener la relación de aspecto.
En el caso de los juegos 3D esto llevará consigo que se verán diferentes zonas.


Unity3d cam rel aspecto 4.jpg


  • Podemos comprobar como quedaría el juego con diferentes relaciones de aspecto en Unity.
Unity3d cam rel aspecto 5.jpg
En este ejemplo, la pantalla donde se probaría el juego tiene una relación 16:9 mientras que el juego se estaría ejecutando con una resolución 4:3 (Unity permite establecer la resolución con la que se juega el juego que no tiene por qué coincidir con la pantalla completa.
Si cambiamos la relación de aspecto a 16:9 vemos que el juego ocupa toda la pantalla (tenéis que cambiar el tamaño de la ventana Game para que el juego ocupe todo):
Unity3d cam rel aspecto 6.jpg



Trabajando con varias cámaras a la vez


Avanzado: Conceptos para programadores

  • Cuando trabajamos sobre el mundo 3D, hablamos de coordenadas (x,y,z) sin embargo nuestra cámara está visualizando 'pixeles' sobre una pantalla 2D (ancho-alto).
Es necesario tener algún mecanismo que nos permita 'pasar' de coordenadas de nuestro mundo (world cooordinates) a coordenadas de nuestra cámara (screen space).
  • Veamos las dos operaciones.


Paso de coordenadas desde ScreenSpace a WorldSpace

  • Imaginemos que queremos obtener la posición de pulsar el botón izquierdo del ratón en nuestro mundo 3D.
Para ello es necesario 'trasladar' nuestro punto (x,y) de la pantalla nuestro mundo 3D, pero necesitaremos indicar el plano sobre el que se va a proyectar.
Recordemos que nuestra cámara tiene dos planos definidos (near - far) pero podríamos proyector el punto sobre cualquier plano entre esos dos puntos (imaginar un cubo al que lo cortáis para para hacer sándwiches, cada corte es un plano al que podéis llevar el punto).


Veamos un ejemplo:
Crear un script de nombre 'UD_CamaraPerspectiva_1' y asociarlo a un cubo en la posición (0,0,0). Situar a la cámara en la posición (0,0,-8) y que se vea el cubo.
Unity3d camPers 1.JPG


Escribir este código en el script:
 1 public class UD_CamaraPerspectiva_1 : MonoBehaviour {
 2 
 3     private Camera cam;
 4     private Vector3 posWorld;
 5 
 6   // Use this for initialization
 7   void Start () {
 8         cam = Camera.main;
 9         posWorld = transform.position;
10   }
11 	
12   // Update is called once per frame
13   void Update () {
14 		
15         if (Input.GetMouseButtonDown(0)) {  // Pulsamos el botón izquierdo del ratón
16             posWorld = cam.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 8));
17             Debug.Log("Posición en el mundo:" + posWorld);
18         }
19         transform.position = posWorld;
20   }
21 }
Si ahora pulsáis en la pantalla podéis ver como el cubo se mueve al punto pulsado.
Nota: transform.position es la posición del cubo. Lo veremos en el siguiente punto.
Fijarse que en el código tenemos puesto 8 como valor z al llamar al método ScreenToWorldPoint. Esto es así ya que la distancia desde la cámara al cubo es de 8 unidades hacia adelante de la cámara (la cámara está situada en la posición -8):
Unity3d camPers 2.JPG


Podemos observar en la pestaña de consola cuales son las coordenadas (x,y,z) de nuestro punto en el mundo en el plano z=8 unidades:
Unity3d camPers 3.JPG


  • Veremos más adelante que Unity (y todos los motores/frameworks) tienen mecanismos para implementar rayos de colisión. Un rayo es un punto con una dirección que atraviesa nuestro mundo 3D. Es como si dispararas una flecha en línea recta desde donde tocas la pantalla hacia el interior de nuestro mundo 3D. Parte del punto de la pantalla donde pulsamos y 'atraviesa' todos los planos de nuestro mundo 3D hasta un plano determinado (normalmente empieza en el plano near de la cámara y acabaría en el plano far de la misma, ya que es el área de visualización) siguiendo una dirección. Más información en este punto.




Paso de coordenadas desde WorldSpace al ScreenSpace

  • En el caso contrario a lo visto anteriormente. Pasamos de coordenados en nuestro mundo 3D a coordenadas del monitor en pixeles.
Debemos de tener en cuenta que para la cámara, la coordenada (0,0) se encuentra abajo-izquierda mientras que las coordenadas en la pantalla, la coordenada (0,0) está arriba a la izquierda. Por tanto, cuando obtengamos la coordenada de pantalla a partir de la coordenada 3D debemos de hacer la operación: Screen_Height-Y.


  • Modificar el código anterior:
 1 public class UD_CamaraPerspectiva_1 : MonoBehaviour {
 2 
 3     private Camera cam;
 4     private Vector3 posWorld;
 5 
 6     // Use this for initialization
 7     void Start () {
 8         cam = Camera.main;
 9         posWorld = transform.position;
10 
11     }
12 
13     private void OnGUI()
14     {
15         Vector2 posPantalla = cam.WorldToScreenPoint(transform.position);
16         posPantalla.y = Screen.height - posPantalla.y;
17         Rect areaVisual = new Rect(posPantalla, new Vector2(50, 20));
18 
19         Debug.Log("Posición en pantalla:" + posPantalla);
20 
21         GUILayout.BeginArea(areaVisual);
22         GUI.color = Color.yellow;
23         GUILayout.Label("CUBO");
24         GUILayout.EndArea();
25 
26     }
27 
28     // Update is called once per frame
29     void Update () {
30 		
31         if (Input.GetMouseButtonDown(0)) {  // Pulsamos el botón izquierdo del ratón
32             posWorld = cam.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 8));
33             
34         }
35         transform.position = posWorld;
36 	}
37 }
  • Línea 15: Pasamos de coordenadas 3D a coordenadas de pantalla (pixeles)
  • Línea 16: Debido a que el punto (0,0) de la cámara es diferente al de la pantalla debemos de restar el alto de la misma.
  • El resto del código ya lo iremos viendo, pero básicamente crea un rectángulo donde dibuja un Label de color amarillo con la palabra 'Cubo'.
Si ejecutáis el código anterior, al pulsar sobre la pantalla podéis ver como el cubo se traslada al punto y aparece la palabra 'Cubo' siempre sobre él:
Unity3d camPers 4.JPG




-- Ángel D. Fernández González -- (2018).