
Las sombras son uno de esos detalles que hacen que una escena 3D resulte creíble y bien asentada, y también de los más fáciles de estropear. Con esta actualización, las sombras del sol son más estables, más limpias y mucho más fáciles de ajustar, y las mismas mejoras llegan a las spot, point y area lights.
En resumen:
- Cascadas estables. Las sombras de las directional lights ya no parpadean cuando la cámara se mueve o gira.
- Un bias más inteligente. Un único ajuste, medido en texels del shadow map, funciona igual de bien desde la cascada más cercana hasta la más lejana.
- Luces sin costuras. Las point lights pierden las costuras del cubo, y las spot, point y area lights comparten el nuevo modelo de bias.
- Más control. Se pueden elegir de 1 a 4 cascadas, cómo se reparten y se ajustan, y cada cuánto se actualizan las lejanas.
- Mejor depuración. Cada cascada y la densidad de texels se ven directamente en el viewport.
En este artículo
- Por qué importan las sombras
- Cómo funciona una sombra en tiempo real
- El problema del sol y las cascadas
- Dónde se quedaba corto el enfoque anterior
- Novedades
- El antes y el después
- Primeros pasos
- Actualización desde la versión anterior
- Pruebas y comentarios
Por qué importan las sombras
Sin sombras, una escena parece flotar. Los objetos pierden su conexión con el suelo, cuesta más leer la profundidad y la iluminación se ve plana. Una buena sombra indica dónde está cada cosa, qué tamaño tiene y de dónde viene la luz.
Una mala sombra se nota igual de rápido: bordes que se arrastran cuando se mueve la cámara, rayas sobre una superficie o una sombra que se despega del objeto que la proyecta rompen la ilusión al instante. Eso es justo lo que ataca esta versión.
Cómo funciona una sombra en tiempo real
Una sombra real aparece porque algo bloquea la luz, pero un motor en tiempo real no traza la luz. Recurre a un truco más barato y muy eficaz, el shadow map:
- Mirar desde la luz. Se renderiza la escena desde el punto de vista de la luz, pero de cada píxel solo se guarda la distancia a la superficie más cercana. El resultado es el shadow map.
- Mirar desde la cámara. Para cada píxel de la pantalla se calcula dónde queda visto desde la luz y se compara su distancia con la que registró el shadow map.
- Decidir. Si el shadow map registró algo más cerca de la luz, el píxel está en sombra. Si no, está iluminado.
El problema es que un shadow map es una textura de resolución fija: es como una foto de baja resolución de la escena, tomada desde la luz. Casi todas las técnicas de sombras son, en el fondo, formas de sacarle el máximo partido a esa resolución limitada.
¿Y las sombras con ray tracing y path tracing?
Los shadow maps son una aproximación, así que es lógico preguntarse por qué no se traza directamente la luz. En GPUs con ray tracing por hardware se puede: se lanza un rayo desde cada punto visible hacia la luz y, si algo se interpone, el punto está en sombra. No hay resolución de textura que se agote, así que no hay bloques, ni acne, ni peter-panning, y las sombras suaves salen con precisión. El path tracing va un paso más allá y sigue muchos caminos aleatorios de luz mientras rebotan por la escena, de modo que sombras, reflejos e iluminación indirecta salen del mismo proceso. El sample de path tracer de Evergine muestra cómo se ve.
El precio es el rendimiento. Una imagen limpia necesita varios rayos por píxel en cada frame, normalmente seguidos de un denoiser. La escena necesita una estructura de aceleración que hay que reconstruir cuando los objetos se mueven. Y los rayos se dispersan en todas direcciones, lo que castiga mucho más a la memoria de la GPU que rasterizar triángulos sobre una textura. Incluso las GPUs de escritorio más potentes reparten sus rayos con cuidado, y la mayoría de móviles, tablets y dispositivos modestos no tienen ray tracing por hardware.
Por eso los shadow maps siguen siendo el caballo de batalla de las sombras en tiempo real: son baratos, predecibles y funcionan en prácticamente cualquier dispositivo. El ray tracing es una gran opción donde el hardware lo permite, pero los shadow maps son lo que mantiene una escena funcionando en todo lo demás, y por eso seguimos invirtiendo en ellos.
El problema del sol y las cascadas
Una directional light hace de sol, y el sol lo ilumina todo. Un único shadow map tendría que cubrir todo el mundo visible, así que una textura estirada sobre cientos de metros daría sombras pixeladas justo delante del jugador, que es donde más importa el detalle.
La solución clásica son los cascaded shadow maps. Se divide la vista en franjas según la distancia a la cámara: una pequeña cerca de la cámara, otra más grande detrás, y así sucesivamente. Cada franja tiene su propio shadow map, de modo que las sombras cercanas son nítidas y las lejanas más toscas. Lo lejano se ve pequeño en pantalla, así que casi no se nota.
Evergine ya usaba cascadas. Lo que cambia con esta versión es que ahora se comportan mucho mejor.
Dónde se quedaba corto el enfoque anterior
Tres problemas aparecían una y otra vez:
- Shimmering. Cada cascada se orientaba según la cámara, así que al girar esta, la cuadrícula del shadow map se deslizaba por el mundo. Los bordes de las sombras se arrastraban y centelleaban.
- Acne y sombras flotantes. Como un shadow map es una cuadrícula de muestras, una superficie puede acabar proyectando sombra sobre sí misma y mostrar rayas (shadow acne). El remedio habitual es un pequeño bias, pero un único valor fijo no sirve para todas las cascadas: si es demasiado bajo, quedan rayas en ángulos rasantes; si es demasiado alto, la sombra se despega de quien la proyecta (peter-panning), sobre todo en las cascadas lejanas.
- Poco margen de ajuste. El número de cascadas estaba fijado en cuatro y había poco control sobre cómo se repartían.
Novedades
Cascadas que ya no parpadean
El espacio de sombras depende ahora solo de la orientación de la luz, no de hacia dónde mira la cámara. Además, cada cascada se ajusta a la cuadrícula de texels del shadow map y su tamaño solo cambia en pequeños saltos de en torno al 2 %. Gracias a eso, mover o girar la cámara ya no cambia qué texel cubre cada parte del mundo, y el shimmering desaparece.
Un bias que entiende la escena
El bias se aplica ahora en espacio mundo y se mide en texels del shadow map. Dos propiedades nuevas, ShadowDepthBias y ShadowNormalBias, se escalan solas según el tamaño de los texels de cada cascada y la suavidad del filtro de sombras. Un único ajuste se comporta ahora con sentido desde la cascada más cercana hasta la más lejana, lo que elimina las bandas de acne en ángulos rasantes y las sombras flotantes en la distancia.
Spot, point y area lights
El modelo de bias basado en texels no se limita al sol: las spot, point y area lights también lo usan ahora, así que ShadowDepthBias y ShadowNormalBias significan lo mismo en cualquier tipo de luz. De paso se han corregido dos problemas:
- Anillos de acne en superficies que miran en sentido contrario al eje de proyección de la luz.
- Costuras visibles en las point lights. Una point light usa seis shadow maps, uno por cada cara de un cubo, y el filtro leía datos incorrectos en los bordes. Ahora cada cara se renderiza algo más ancha que 90 grados, con un margen de 8 texels (la guard band), de modo que las caras vecinas se solapan y las costuras desaparecen. Esta corrección es exclusiva de las point lights.
Cascadas totalmente configurables
Las directional lights exponen ahora el equilibrio entre calidad y rendimiento:
- Cascade count: de 1 a 4. Cada luz reserva solo los shadow maps que necesita.
- Split mode: Automatic mezcla una distribución logarítmica y otra uniforme, controlada con
GammaDistribution. Manual permite decidir dónde termina cada cascada. - Fit mode: Stable (por defecto) ajusta cada cascada a una esfera que envuelve su franja, así que el tamaño del texel nunca cambia. Tight ajusta una caja, con texels en torno a un 20 % más pequeños, a costa de cierto shimmering cuando la cámara gira.
- Selection mode: la cascada de cada píxel se elige según la profundidad de vista, o bien se toma la primera (la más nítida) que lo contiene, para que más píxeles usen los shadow maps de alta resolución.
- Cascade blend y shadow fade: las cascadas vecinas pueden mezclarse entre sí, y las sombras pueden desvanecerse poco a poco antes de la distancia máxima en lugar de cortarse en seco.
- Far cascades update interval: las sombras lejanas cambian despacio, así que esas cascadas pueden renderizarse solo cada N frames. El motor conserva los shadow maps en caché mientras la cámara y la luz se mantengan dentro de una tolerancia, lo que reduce el coste del pase de sombras.
Nota: el update interval no tiene efecto cuando una luz la renderizan varias cámaras.
Mejor depuración
El Debug Mode de las luces muestra ahora el color de cada cascada (y de cada cara del cubo en las point lights), además de un tablero de ajedrez de 8x8 texels del shadow map, que permite ver con exactitud lo densas que son las sombras en pantalla. Las spot lights también tienen ahora su propio DebugMode.
También se han corregido varios problemas relacionados: las distancias de división de las cascadas usan ahora la fórmula correcta, las cámaras ortográficas con el plano near a cero ya no generan divisiones inválidas, y renderizar varias cámaras con varias directional lights que proyectan sombras ya no hace que unas luces pisen los shadow maps de otras. Además, los backends gestionan el depth clamping de forma coherente, de modo que los objetos situados delante de una cascada ya no se recortan.
El antes y el después
Basta con arrastrar el control para comparar el sistema de sombras anterior con el nuevo, en la misma escena y con la misma posición de cámara.
Primeros pasos
Todo se puede ajustar desde el editor o desde código. Los valores siguientes son puntos de partida orientativos que conviene adaptar a cada escena:
// Obtener la directional light de la entidad que hace de sol
var sun = sunEntity.FindComponent<DirectionalLight>();
sun.IsShadowEnabled = true;
sun.ShadowDistance = 100; // Hasta dónde se renderizan las sombras desde la cámara
sun.ShadowCascadeCount = 3; // Entre 1 y 4
// Mantener las cascadas estables y desvanecer las sombras con suavidad
sun.CascadeFitMode = DirectionalLight.CascadeFitModes.Stable;
sun.ShadowFadeFraction = 0.1f;
sun.CascadeBlendFraction = 0.1f;
// El bias se mide en texels del shadow map
sun.ShadowDepthBias = 1;
sun.ShadowNormalBias = 1;
Esto activa un sol que proyecta sombras con tres cascadas estables, una mezcla suave entre ellas y un ligero desvanecimiento en la distancia de sombras. Si aparece acne, conviene subir un poco los bias; si las sombras se ven despegadas de los objetos que las proyectan, bajarlos.
Actualización desde la versión anterior
Algunos cambios afectan a los proyectos existentes:
ShadowBiasqueda obsoleta y se ignora. En su lugar se usanShadowDepthBiasyShadowNormalBias, que se miden en texels, así que los valores antiguos no se trasladan directamente.- Los shaders propios que llamen a
DirectionalLightShadow,PointLightShadowoSpotLightShadowreciben ahora la normal geométrica en lugar deNoL. - Si un shader propio lee
LightProperties.CascadeSplits, hay que tener en cuenta que ahora es unfloat4con los límites de las cascadas. El primer valor es donde empiezan las sombras y los tres siguientes son donde terminan las tres primeras cascadas.
Pruebas y comentarios
¿Listo para dar a las escenas sombras más estables y limpias? Basta con actualizar Evergine, abrir una escena con una directional light y probar las nuevas opciones.
- La documentación de Evergine incluye la referencia completa de luces y sombras.
- Los samples de Evergine muestran luces y sombras en escenas reales.
- ¿Se ha encontrado un problema o hay una idea? Se puede compartir en el repositorio de feedback de Evergine.
Nos encantará conocer la opinión de la comunidad.
