lunes, 28 de julio de 2014

Las sombras del veneno II: Implementación

Esta va a ser la primera entrada algo técnica del blog. Tampoco mucho, la mayoría de conceptos se pueden entender siguiendo la lectura, aunque con conocimientos sobre generación de sombras y algo de Unity se saca todo el jugo.

Proyector de Unity


En la primera parte del artículo sobre las sombras del veneno, hablaba sobre la idea de que la sombra de un objeto fuera diferente a la que se espera que proyecte como recurso gráfico y narrativo.

Todo tenía buena pinta, pero llega el momento de ¿y eso cómo lo hago?. Partimos de la primera idea, que el héroe tuviera la sombra de otro ser. Por ejemplo que la sombra tuviera alas, como si fuera un Ángel.

Estuve dando vueltas a esto. Una idea es hacer un modelo del personaje con alas, hacer una capa de render donde se pinte ese modelo alado, y en los shaders de esta pasada ignorar el modelo y usarlo solamente para pintar la sombra. Y en una segunda capa cambiar el modelo por el personaje normal y pintarlo, esta vez sin sombra.

Técnicamente es posible, y puede que use esta técnica alguna vez para lograr algún efecto, pero es una técnica compleja. Hay que tener los 2 modelos, tener los dos animados, animar los dos a la vez (es decir si el modelo normal levanta el brazo, que el modelo alado también lo levante para que la sombra salga con el brazo levantado), sincronizar el movimiento de ambas, hay que crear la forma de hacer esas 2 pasadas de render y hacer los shaders apropiados. Y además el cambio de un modelo a otro y tener 2 pasadas puede ser computacionalmente crítico. Sobre todo en dispositivos móviles, donde la memoria de vídeo está bastante limitada.

La segunda forma de hacer esto es pre-calcular la sombra para la animación interna y poner un objeto plano que sea la sombra. Lleva más trabajo previo, pero es computacionalmente más barato. La idea sería tener una imagen de la sombra para cada frame de animación interna. Se podría incluso pintar a mano sin llegar a modelar la versión del héroe con alas. Habría que cambiar la imagen de la sombra con cada frame de animación y moverla y colocarla de forma correcta según el movimiento externo del personaje.

Y aquí vienen los líos. Lo primero, ¿dónde está el suelo?. Si el personaje no se moviera del suelo podríamos poner la sombra a sus pies, pero el personaje salta y sería muy raro que la sombra “saltase” con el personaje ¿no?. Así que tenemos que calcular la altura del suelo para colocar allí la sombra (realmente un poquito más arriba para evitar parpadeos por problemas de precisión). Si tuviéramos todo el suelo de la pantalla sería pan comido, un valor fijo, pero vamos a suponer que vamos a querer plataformas a varias alturas, cuestas... un escenario complejo. Podríamos tomar la altura del suelo tirando un rayo desde el personaje hacia abajo (con una máscara para solo comprobar colisiones contra suelo, aunque esto también lleva a errores), y donde colisione es suelo. El mayor problema viene cuando la sombra no está entera sobre un solo suelo. Por ejemplo si estamos saltando hacia un precipicio para llegar a una plataforma, puede que media sombra quede en el suelo y media en el aire, pero es muy extraño ver una sombra flotando en el aire. Esto se podría solucionar comprobando bit a bit en la sombra si está sobre el terreno, pero así no ganamos respecto a calcularlas respecto a tiempo real. Se pueden hacer muchas mejoras, algoritmos excelentes, usar herencia de polígonos contenedores en la sombra por ejemplo. Pero lo que quiero hacer ver es que lo que parecía una solución muy sencilla se complica mucho.

Y aquí llegamos a las simplificaciones que asumí y la solución que tomé al final. Hay que tener en cuenta que tenía que entregar el juego para la asignatura, con poco tiempo y tenía que hacer un balance para conseguir los efectos que quería sin morir en el intento. Lo primero decidí cambiar quien tiene la sombra extraña, y ya no es el héroe sino el veneno. Esto es porque el veneno no tiene animación interna. No mueve brazos ni nada así, por lo que solo hay que ver donde va la sombra, si es más grande o más pequeña (según la altura del veneno o su distancia a la luz), pero puede hacerse siempre con la misma imagen. Ya comenté en el artículo anterior las 4 sombras y las situaciones en que se daban.

La solución por la que opté al final fue usar un proyector. Es un componente de Unity que permite proyectar una imagen. Se configura de forma similar a una cámara, con planos de proyección, aspect-ratio, etc y le ponemos la imagen que queremos que proyecte como un material (así que podría ser textura, textura procedural, video...). Este sistema se está usando mucho en juegos para móviles, por ejemplo creo que se usa en el juego Angry Birds Go, por como se ven las sombras.

Tiene sus cosas buenas y malas. Lo bueno es que la sombra queda automáticamente donde debería. Es decir, si hay unos escalones, se va adaptando a esa forma, o si hay objetos entre el proyector y el suelo. Que la sombra sea correcta o creíble depende de la imagen que nosotros le pongamos. Es un método computacionalmente mucho más barato que calcular las sombras en tiempo real, y por eso se usa mucho en móviles. No tiene en cuenta la luz, es ideal para una escena iluminada por luz direccional. Ya que si tenemos una luz puntual o varias tendríamos que ir cambiando la orientación del proyecto. Pero no tiene en cuenta oclusiones, si el personaje se mete debajo de un saliente o se acerca a una pared y no le da la luz emitirá sombra igualmente. Pero bueno, la mayoría de métodos de iluminación en tiempo real son locales y no tienen esto en cuenta tampoco.

El otro gran problema lo podemos tener con la orientación. Si ponemos el proyector en el objeto de nuestro personaje, se moverá con él. Imaginemos por ejemplo que ante cierto golpe el veneno gira dando una vuelta de campana, el proyector también giraría y la sombra pasaría a desaparecer del suelo para proyectarse por paredes y techo, como si el sol estuviera girando alrededor. Por eso es recomendable aislar el proyector del modelo, ponerlo en otro objeto, preferiblemente el padre del objeto que tenga el modelo y realizar las traslaciones en el objeto proyector, pero las rotaciones en el objeto del modelo.

Conclusiones: El proyector es un gran elemento para hacer sombras sencillas, sin animación interna, a bajo coste. Tenemos libertad creativa en esas sombras, y todo desde la interfaz de alto nivel. Pero si hago algo parecido para el personaje principal tendré que usar otro método a bajo nivel.

No hay comentarios :

Publicar un comentario