Prueba de iluminación dinámica 2D

Hace un tiempo atrás, me interesé por el uso de iluminación dinámica 2D que sería utilizada como un recurso en la programación de juegos HTML5.

Claro, que hay varias opciones, pero la idea principal también es la de aprender mientras se hace, por lo que me propuse a realizar un motor de iluminación dinámica 2D.

Bueno la palabra “motor” a lo mejor le queda muy grande, más bien se trata de un experimento.

El ejemplo en cuestión

Puedes ver el código en: https://github.com/2012lucho/phaser-2d-dinamic-lights

Aquí mismo agrego un ej del mismo en funcionamiento:

En dicha demo, definí dos luces que permanecen inmóviles en su posición, una de ellas es de color verde la otra celeste; adicionalmente agregué tercera luz de color amarillo que se mueve siguiendo la posición del mouse.

¿Como generar el efecto de sombras?

Buscando la manera más sencilla y que a su vez consuma la menor cantidad de recursos posible, se me ocurrió que una buena forma de simular las luces y sombras sería el de utilizar una nueva imagen superpuesta, la cual funciona como si se tratara de una máscara (se le llamará así de ahora en adelante).

Inicialmente dicha imagen debería ocupar el tamaño total de la zona visible y estar siempre por encima de todos los objetos de la escena.

También todos sus pixeles serían negros y con la opacidad al 100%.

Luego se simula el trazado de los rayos de luz de cada una de las luces, de forma secuencial, de forma que cada una de ellas se dibuja en orden.

En cada posición en donde se determina que pasa el rayo de luz, se modifica el pixel correspondiente de la máscara redefiniendo su color y su valor de opacidad, de forma que dicho pixel ahora tenga cierto grado de transparencia, permitiendo ver así los objetos que se encuentran por debajo.

En las posiciones en donde los rayos de luz se superponen, se realiza la suma de sus colores en proporción a un valor correspondiente a la intensidad que tendría la luz en dicha parte del recorrido, para imitar su comportamiento natural.

También hay que tener en cuenta que no pretende ser una simulación 100% realista.

¿Por que se ve tan pixelado?

Seguro sea una de las primeras cosas que salta a la vista, bueno eso se debe a la forma en la cual funciona, por que para cada fotograma se deben hacer miles de cálculos para simular los trazos que realizarían los rayos de luz.

Esto si se hace en una GPU no supondría gran problema ya que están pensadas para ello, pero en este caso los cálculos los realiza un único núcleo de CPU.

Por lo que la forma más fácil que se me ocurrió, para que el sistema de iluminación no consuma grandes recursos, es el de bajar la resolución con respecto al cálculo de luces.

Por lo que la máscara en este caso es 5 veces menor que el área visible, y luego se re-dimensiona para que ocupe la totalidad de la superficie.

Así como también se redució la resolución de la simulación, también se puede definir que la misma no se realice en todos los fotogramas, de forma de minimizar aún más la carga de trabajo.

¿Y los obstáculos?

Sin obstáculos, la luz avanza en linea recta hasta el infinito, en este caso avanza hasta salirse de la pantalla o hasta chocar con un obstáculo.

Por lo que se hace necesario también definir una especie de “mapa de durezas”, por lo que se define una matriz del mismo tamaño que la cantidad de píxeles de la máscara.

Y la idea principal, es que todos aquellos objetos del juego que interactúen con la luz actualicen el mapa de durezas.

Indicando si se trata de un pixel opaco o no, aunque también se podría mejorar de forma de definir grados de transparencia, indice de refracción, etc.