Luces 2D y sombras

Introducción

En este tutorial se explica cómo funciona la iluminación 2D en la demo lights and shadows . Comienza con una descripción breve de los recursos usados en la demo y describe cómo hacer una escena similar paso a paso.

../../_images/light_shadow_main.png

Todos los recursos para este tutorial se pueden encontrar en el repositorio oficial de demos en github. Se sugiere descargarlo antes de comenzar. Alternativamente el mismo proyecto puede ser descargado desde el Administrador de Proyectos. Inicia Godot y en la barra superior selecciona «Templates» y busca «2D Lights and Shadows Demo».

Organización

Para esta demos usaremos 4 texturas: dos para las luces, una para los generadores de sombra y una para el fondo. Se incluyeron links a todos los recursos por si se desea descargarlos por separado de la demo.

Lo primero a colocar es la imagen de fondo (bg.png) usada en la demo. No es necesario tener un fondo pero lo usaremos para la demostración.

El segundo es una imagen negra simple (caster.png), la usaremos en el objeto generador de sombra. PAra un juego top down, esto podría ser una pared o cualquier otro objeto que haga sombra.

Lo siguiente es la luz propiamente dicha (light.png). Si haces clic en el link, notarás lo grande que es. La imagen usada para la luz debe cubrir la misma área que se quiere afectar por la luz. Esta imagen es 1024x1024, así que se usará para cubrir 1024x1024 pixeles en el juego.

Finalmente tendremos la imagen del «foco» (spot.png). La demo usa un círculo para mostrar dónde están las luces y la imagen más grande aplica el efecto de la luz en el resto de la escena.

Nodos

La demo utiliza cuatro nodos diferentes:
  • CanvasModulate
  • Sprite
  • Light2D
  • LightOccluder2D

El CanvasModulate es usado para oscurecer la escena.

Los Sprite son usados para mostrar las texturas de los puntos de luz, el fondo y los generadores de sombra.

Los Light2D son usados para iluminar la escena. El modo en que una luz trabaja es sumando la textura seleccionada al resto de la escena para simular la iluminación. Pero puede ser usada de otros modos, por ejemplo como máscaras para ocultar parte de la escena.

Los LightOccluder2D se utilizan para decirle al shader qué parte de la escena debe producir sombra. La sombra aparece sólo en areas cubiertas por el Light2D y su dirección está basada en el centro del Light2D.

Luces

Los Lights cubren la total extensión de la textura correspondiente. Usan mezcla aditiva para agregar color de su textura a la escena.

../../_images/light_shadow_light.png

Lights Tiene 4 modos en la propiedad Mode: Add, Sub, Mix, and Mask.

Add suma el color de la textura de la luz a la escena. Esto ilumina el área bajo la luz.

Sub resta el color de la luz en la escena. Esto oscurece el área bajo la luz.

Mix mezcla el color de la luz con la escena cubierta. Esto resulta en una iluminación que está entre el color de la luz y el de la escena que se encuentra debajo.

Mask es usada para enmascarar áreas que están cubiertas por la luz. Las áreas enmascaradas serán ocultadas o reveladas basado en el color de la luz.

Para la demostración, las luces tienen dos componentes, la Light (luz) que representa el efecto de iluminación, y un Sprite que es una imagen que muestra el origen de la luz. Un hijo Sprite no es necesario para que un nodo Light2D funcione.

../../_images/light_shadow_light_blob.png

Sombras

Las sombras se hacen mediante una intersección de un Light con un LightOccluder2D.

Por defecto, las sombras están apagadas. Para encenderlas debes hacer clic en el nodo Light y en la sección Shadows marcar Enabled.

En la demo se usa un Sprite con una textura para hacer los «generadores de sombra» pero, en realidad, todo lo que necesitas es unos cuantos nodos LightOccluder2Ds. Por sí mismo, el LightOccluder2D se ve como un punto oscuro y en la demo, el Sprite es sólo un cuadrado negro.

Paso a paso

Ahora que hemos cubierto lo básico de los nodos que usaremos, podemos ir paso a paso en el proceso de hacer una escena como la de la demo.

Primero agrega un Sprite y asigna su textura a background image. Para tu juego, esto puede ser cualquier otra imagen de fondo. Para este estilo sería algo como una textura de piso.

../../_images/light_shadow_background.png

Luego crea tres Light2D’s y asigna en sus texturas a light image. Puedes alterar el color en la sección superior. Por defecto las sombras están apagadas y el mode en tipo add. Lo que significa que cada luz agrega su propio color a lo que esté al fondo.

../../_images/light_shadow_all_lights_no_blob.png

Como paso siguiente agregaremos un hijo Sprite a cada nodo Light, y asignaremos en su textura la blob image. Cada uno de esos deberá estar centrados en el nodo Light muestra el efecto de la luz sobre la escena. El LightOccluder2D’s tomará como centro de la luz a la posición del nodo Light, es por eso que queremos que el Sprite esté centrado en el padre.

../../_images/light_shadow_all_lights.png

Nota

Al momento de escribir esto, 3.0 es la versión estable. La versión 3.1 en desarrollo contiene muchos cambios al sistema de animación así que las animaciones de la demo no estarán cubiertas por este tutorial. Ver Introducción a las características de la animación 2D para más información.

En este momento la escena se ve muy brillante. Esto es porque las tres luces están sumando su color a la escena. Es por ello que la demo usa un CanvasModulate en la escena. El CanvasModulate multiplica todo el Viewport por un color determinado.

Agrega un CanvasModulate a la escena y cambia su color a rgb(70, 70, 70). Esto hará que la escena se vea lo suficientemente oscura para ver los efectos de las distintas luces.

../../_images/light_shadow_ambient.png

Ahora crearemos los generadores de sombra.

La demo usa un Node llamado «casters» para organizar los nodos que proyectarán sombras. Entonces agrega un Node a la escena. Este será usado para agrupar todos los generadores de sombra. De este modo podremos mostrarlos y ocultarlos a todos al mismo tiempo.

Cada generador de sombra está compuesto de un ref:Sprite <class_Sprite> que posee un LightOccluder2D de hijo. Para la demo, el Sprite tiene una textura asignada a caster image y nada más. El hijo LightOccluder2D es donde sucede toda la magia. En un juego, el Sprite puede ser otra cosa que una caja negra; podría ser una imagen de cualquier objeto que produzca sombra: una pared, un cofre mágico, etc.

../../_images/light_shadow_sprites.png

El LightOccluder2Ds le dice al juego cuál es la forma del obstructor de luz. Posee un OccluderPolygon2D, el cual es un contenedor para un polígono y otros datos. Para esta demo, como nuestra pared es cuadrada, dibujamos un cuadrado. Las otras opciones por defecto están bien.

La primera de las opciones, Closed puede estar en on o en off. Un polígono cerrado obstruye la luz que viene desde todas las direcciones, un polígono abierto lo hace desde una sola dirección

Cull Mode te deja seleccionar de qué dirección debe aplicarse. Por defecto es Disabled, lo que significa que el elemento que obstruye generará una sombra sin importar de qué lado venga la luz. Las otras opciones Clockwise (sentido horario) y Counter-Clockwise (sentido antihorario) se refiere al orden en que se dibujaron los vértices del polígono. Este orden determina qué lado de la línea está dentro del polígono. Sólo las líneas que miran hacia afuera son las que proyectan sombras.

Para ilustrar la diferencia, aquí hay una imagen de un LightOccluder2D con Closed puesto en off y el correspondiente OccluderPolygon2D, así las líneas del polígono pueden verse:

../../_images/light_shadow_cull_disabled.png

Nota

Cull Mode está marcado como Disabled. Las tres líneas hacen sombra.

../../_images/light_shadow_cull_clockwise.png

Nota

Cull Mode está en Clockwise. Sólo la línea de arriba y de la derecha hacen sombra.

../../_images/light_shadow_cull_counter_clockwise.png

Nota

Cull Mode marcado como Counter-Clockwise. Sólo la línea de abajo genera sombra. Si Closed se coloca en on veremos una línea vertical adicional a la izquierda que podría generar sombra también.

Cuando agregues el LightOccluder2Ds, las sombras no aparecerán. Tienes que volver al Light2Ds y en la sección Shadow marca Enable como on. Esto «enciende» las sombras con bordes duros como en la imagen de abajo.

../../_images/light_shadow_filter0_pcf0.png

Para hacer que las sombras tengan un lindo borde suave, ajustaremos las variables filter (filtro), filter smooth (suavizado) y gradient length (longitud del gradiente). Godot soporta Percentage Closer Filtering (PCF), el cual toma muestras múltiples del mapa de sombra alrededor de n pixel y los difumina para crear un efecto de sombra suave. Mientras mayor sea el número de muestras, más suave será el sombreado, pero se ejecutará más lento. Esto es porque Godot provee de 3 a 13 muestras por defecto y te permite elegir. La demo utiliza PCF7.

../../_images/light_shadow_normal.png

Nota

Esta es una sombra dibujada con la configuración de la demo. gradient length en 1.3, filter smooth puesto en 11.1 y filter en PCF7.

../../_images/light_shadow_pcf13.png

Nota

filter está puesto en PCF13. Nota cómo la sombra se vuelve más ancha, esto es porque la distancia entre muestras está basada en el filter_smooth.

Para hacer uso del filtro se necesita usar la variable filter_smooth. Esta indica cuán aparte deben estar las muestras una de otra. Si quieres que el área suave se extienda mucho, puedes incrementar el tamaño del filter smooth. Sin embargo, con pocas muestras y un gran filter smooth, podrás ver que se forman líneas entre las muestras.

../../_images/light_shadow_filter30.png

Nota

filter smooth está puesto en 30.

Los distintos nodos Light en la demo utilizan diferentes valores para filter smooth. Juega con los valores para ver cuál te gusta.

../../_images/light_shadow_filter0.png

Nota

filter smooth está en 0.

Finalmente, veamos la variable gradient length. Para algunas sombras suaves, es preferible que una sombra no comience directamente en el objeto, esto produce un borde duro. la longitud del gradiente crea un gradiente suave al principio de la sombra para reducir el efecto del borde duro.

../../_images/light_shadow_grad0.png

Nota

gradient length está en 0.

../../_images/light_shadow_grad10.png

Nota

gradient length está puesto en 10.

Necesitarás probar un poco las opciones para encontrar la que se ajuste a tu proyecto. No hay una solución correcta para todos, es por ello que Godot provee mucha flexibilidad. Ten en mente que mientras filter sea mayor, más costosa será la generación de sombras.