Jorge
Jorge Autor de Aprende Unreal Engine 4.

Las 50 sombras de Unreal

Las 50 sombras de Unreal

En UE4 hay muchas técnicas para sombreado: sombras bakeadas, stationary light shadows, dynamic shadows, directional light cascading shadow maps, distance field shadows, cascading shadow maps, contact shadows, ray traced distance field soft shadows, distance field indirect shadows y quizás alguna otra. Casi todas son complementarias y sirven para distintos propósitos. Por defecto hay activadas algunas y otras están desactivadas. Como ves todo un arsenal de sombras, veamos como funcionan y cuál debemos escoger en cada momento.

El algoritmo base para las sombras en UE4 es el Distance Field Shadows (también llamado distance field shadowmaps). Está basado en el cascading shadow maps. Más info del CSM aquí. Y muchas técnicas se apoyan en él.

¿En qué es mejor el distance field shadowmaps del CSM? Es una mejora, propuesta por Valve, del algoritmo clásico de shadow casting. En el shadow casting clásico, a cada pixel se le asocia sombra (1) o sin sombra (0). En el distance field shadowmaps a cada pixel se le asocia la distancia a la sombra más cercana. De este modo, las sombras tienen mejor calidad porque se puede hacer una transición entre sombra y sin sombra. También funciona muy bien en bajas resoluciones.

De todas esas técnicas. Las cuatro técnicas principales de sombreado son: las static lights, los directional light cascading shadow maps, las stationary light shadows y las dynamic shadows.

Static Lights.

Las static lights hacen sombras bakeadas, sombras gratis. Obviamente los actores movables no proyectan sombra* ya que la luz sencillamente “no existe”, está bakeada en la textura. Requiere “cocinado”, light building.

En las static lights se pueden suavizar las sombras usando la propiedad Light Source.

Shadows Light Source

La luz de la izquierda tiene menos Light Source que la luz de la derecha.

En la mayoría de ocasiones serán los componentes StaticMeshComponent, que son los responsables de renderizar los static mesh, quienes en última instancian reciben la sombra que proyecten otros objetos. Una propiedad importante de las static light son las Lightmap Resolution sobre dichos componentes que permite controlar el nivel de detalles de las sombras.

* Existen dos técnicas para que los actores movables proyecten sombra bajo luces estáticas: Capsule Shadow para los SkeletalMeshComponent y Distance Field Indirect Shadows para los StaticMeshComponent. Lo discutiremos más abajo.

Directional Light Cascading Shadow Maps

Hay cuatro tipos de luces: Directional, Point, Spot y Sky. Y tres modos: static, stationary y movable. Este tipo de sombra solo se dan en las luces Directional Stationary.

Las luces direccionales estacionarias son especiales porque soportan toda las sombras de la escena a través de Cascaded Shadow Maps al mismo tiempo que soporta sombras estáticas. A partir de una determinada distancia todas las sombras son estáticas mientras que menos de esa distancia son dinámicas, la transición es indistinguible.

Es muy útil en niveles con mucho follaje animado dónde quieres sombras moviendo alrededor del personaje pero no pagar el coste de tener que calcular las sombras del follaje lejano.

Se puede setear la distancia de dicha transición cambiando la propiedad Dynamic Shadow Distance StationaryLight.

Se puede cambiar las sombras estáticas por Ray Traced Distance Field Shadows, de manera que la transición sea de CSM a RTDF. Más info más abajo.

Por otra parte, las luces direccionales estacionarias, a partir de la versión 4.9, tienen disponible Area Shadows (se activa con un checkbox) que ofrecen sombras más suaves:

Shadows Area

Stationary Light Shadows

Las luces stationary proyectan sombras estáticas para los objetos estáticos y sombras dinámicas (distance field shadowmaps) para los objetos dinámicos (como los StaticMeshComponents o SkeletalMeshComponent con mobility en Movable).

Bajo una luz dinámica (es decir, luz movable) todos los objetos (estáticos o movables) proyectan sombra y, por tanto, un objeto movable recibe la sombra que proyecta un objeto estático bajo una luz movable, se trata de una sombra dinámica. En las luces estáticas, en cambio, se bakean las sombras y los objetos movable no solo no proyectan si no que no reciben la sombra que proyecta un objeto estático.

El caso de las luces estacionarias es complicado, porque la sombra que proyecta un objeto estático bajo una luz stationary es una sombra estática (está bakeada) y el objeto movable DEBE recibir dicha sombra. Para ello, las luces estacionarias soportan un nuevo tipo de sombra las stationary light shadows:

Shadows Stationary

En la imagen superior la pared es estática así que la sombra proyectada bajo una luz stationary es estática (está bakeada). La esfera es movable y la sombra que proyecta bajo una luz stationary es dinámica.

Shadows Stationary

En esta imagen es dónde ocurre la “magia” de las luces stationary. El objeto es movable y sin embargo está recibiendo sombra estática, cosa que no ocurriría si la luz fuera estática**. Eso es debido a que bajo las luces stationary todos los objetos movables tienen dos luces dinámicas, una proveniente del mundo “estático” y otra la luz dinámica que proyecta, el engine se encarga de mezclar ambas.

En otras palabras, se hace sombra por objeto dinámico (per object shadows). Para cada objeto dinámico el engine crea dos sombras dinámicas por cada luz estacionaria: una sombra para gestionar las sombras que recibe (casting) del mundo estático y otra para gestionar las sombras que recibe del mundo dinámico. Esto significa que el coste puede variar en función de cuantos objetos dinámicos hay. Con suficientes objetos dinámicos, es más eficiente usar luces dinámicas que estacionarias.

** Si bajo esa misma escena seteas la luz a estática verás como la esfera parece que sigue “recibiendo” la sombra (en realidad no recibe sombra alguna, lo que recibe es menos luz y parece más oscura) y cuando la mueves a una zona iluminada la esfera reciba dicha luz. Es como si la luz estática fuera dinámica. Lo que ocurre es que la esfera movable recibe la luz de su volumetric sample. Más info aquí: UE4 Lighting & Lighmass

Dynamic Shadows

Las sombras dinámicas no están bakeadas en absoluto y dan sombra a TODO (static o movables). Son las más caras. Cuantos más elementos tengan que sombrear, más caras son.

Para las luces dinámicas UE4 usa Shadow Map Caching para mejorar el rendimiento. Esto es, si la luz dinámica junto con el resto de elementos que sombrea no se mueven en un determinado frame se reutiliza las sombras calculadas del frame anterior.

Se puede medir el rendimiento de las sombras con el siguiente comando de la consola:

stat shadowrendering

En concreto, las estadísticas relevantes son CallCount y InclusiveAug.

Y puedes activar/desactivar el shadow map caching con:

r.Shadow.CacheWholeSceneShadows 0

Preview Shadows

Son las sombras que ofrece el engine cuando aún no se ha hecho el build lighting.

Capsule Shadows

En entornos dónde sólo hay luz indirecta (estática / bounce) los objetos movables no proyectan sombra. Por ejemplo bajo una luz estática.

Las capsule shadows sirven para que los Skeletal Mesh Component (por ejemplo el de un character) proyecte sombra bajo luz estática. Para los Static Mesh está la técnica Distance Field Indirect Shadows (ver más abajo).

La técnica capsule shadows usa las cápsulas de colisión de sus respectivos Physics Asset junto a la información sobre la dirección e intensidad de la luz que provee los volumetric samples más cercanos para proyectar su sombra.

Capsule Shadows

Para activar este modo de sombreado, en el componente skeletal mesh en el apartado lighting añadir el physics asset:

Capsule Shadows Settings

Este modo de sombreado es capaz de proveer sombras dónde el método estándar no podría. En concreto activando el capsule indirect shadow utilizará la información de los volumetric sample para proyectar sombra.

Capsule Shadows Enabled

Aquí un how-to de la documentación de Unreal.

Contact Shadows

Capsule Shadows Enabled

En las luces hay una propiedad llamada Contact Shadow Length. Sus valores van de a . Cuando la activas () el engine traza ray desde el pixel a la posición de la luz y utiliza este ray para ver si el pixel está ocluido (el ray ha colisionado con algo antes de llegar a la luz) y por tanto este pixel tiene sombra. El valor Contact Shadow Length es un factor del valor máximo de depth buffer, es decir, el valor 1 significa que el rayo puede tener una longitud máxima que atraviese la pantalla completa, un valor de 0.5 el rayo solo atravesaría la mitad. Un buen valor suele ser 0.1.

Es importante subrayar el hecho de que el número de samples que toma el engine a lo largo del ray es el mismo independientemente de su longitud, por lo que a mayor longitud mayor número de potencial artefactos.

Ray Traced Distance Field Shadows (RTDF)

Esta técnica de sombreado está disponible para luces movables. Esta técnica necesita tener computados los Mesh Distance Fields (también llamado Signed Distance Fields) para cada static mesh. Calcular los SDF por cada static mesh está desactivado por defecto, hay que activarlo en Settings > Rendering > Calcualte Mesh Distance Fields (requiere reiniciar el editor).

¿Qué es un SDF? Un SDF almacena para cada punto del mesh la distancia a la superficie más cercana. Los puntos interiores almacenan distancia negativa.

Signed Distance Fields

Aparte de esta técnica de sombreado, los SDF se pueden usar para muchas cosas como por ejemplo: dynamic ambient occlusion, GPU particle collision, dynamic flow maps y otros.

Una de las propiedades del SDF es que, gracias a él, hacer un trace cone tiene el mismo coste que un trace ray. Más detalles en la documentación oficial.

La idea básica que hay detrás del RTDF es trazar un ray desde cada pixel sombreado a través del SDF hasta la fuente de luz. Usar la distancia al objeto más cercano (info que provee el SDF) que ocluye dicho pixel para aproximar un trace cone (con el mismo coste que un trace ray gracias al SDF). Y con ese trace cone se pueden conseguir areas sombreadas de alta calidad que provengan de luces esféricas.

Con el método tradicional:

Traditional Shadow Maps

Las sombras más alejadas no son realistas porque en la vida real deberían ser mucho más suaves. Con RTDF al usar trace cone las sombras quedarían realistas:

Traditional Shadow Maps

Para activar las RTDF a una luz dinámica usar la propiedad RayTraced DistanceField Shadows.

RTDF Settings

Esta técnica es muy eficiente para largas distancias porque solo tiene en cuenta los pixeles visibles. La propiedad DistanceField Shadow Distance puede ser usada para discriminar cuando usar RTDF. Por ejemplo, en una directional light se puede usar Cascade Shadow Maps para cortas distancias y usar RTDF para largas distancias:

Shadows CSM

Shadows RTDF

Se puede ver una representación gráfica de Mesh Distance Field en Show > Visualize > Mesh DistanceFields.

Show Mesh Distance Fields

Quizás para algunos static mesh haya que ajustar su escala para calcular correctamente su SDF. En el editor del static mesh:

Mesh Distance Fields Settings

Mesh Distance Fields Scale

Para las point light y spotlight la propiedad Source Radius es usada para determinar la penumbra de las sombras.

Mesh Distance Fields Scale

Distance Field Indirect Shadows

Esta técnica (nueva en 4.18) es la equivalente a Capsule Shadows, que se usaba para los Skeletal Mesh Component, pero para Static Mesh Component.

En concreto, esta técnica trabaja de manera similar a Capsule Shadows ya que usa los volumetric samples generados durante el lighting build para proyectar sombra de Static Mesh Components movables bajo luces estáticas pero en este caso, en vez de usar las cápsulas del physic asset que un static mesh no posee, utiliza su Mesh Distance Field.

A diferencia de RTDF no es necesaria calcular todo el SDF de toda la escena, basta con calcular el mesh distance field para ese static mesh.

Mesh Distance Fields Scale

Una vez colocado el static mesh component con dicho static mesh, para activar el distance field indirect shadow en la luz basta con activar el checkbox correspondiente (recuerda que solo tiene sentido en objetos movables):

Shadow Distance Field Indirect Shadow Checkbox

Ambient Occlusion

Bajo la pestaña World Settings en Lightmass puedes activar la generación del ambient occlusion estático.

En 4.19, existe la posibilidad de tener ambient occlusion dinámico. Se necesita como requisito tener calculado Mesh Distance Fields (Settings > Rendering > Calcualte Mesh Distance Fields).

Una vez calculados los Mesh Distance Fields, se usa una luz Skylight en modo movable. Se activaran los ajustes para el AO dinámico:

Ambient Occlusion Dinámico

comments powered by Disqus