Motor WebGPU desde cero Parte 9: Mapas de Sombras

En este artículo explico de forma práctica y traducida el enfoque clásico para generar sombras en gráficos 3D usando mapas de sombras y cómo lo implementé en un motor WebGPU desde cero. Existen dos aproximaciones comunes para sombras: el trazado de rayos, que es preciso pero muy costoso, y el mapeo de sombras, que es económico y ofrece resultados suficientemente buenos para videojuegos y aplicaciones interactivas. El mapeo de sombras es lo que verás en la mayoría de juegos y produce artefactos característicos como sombras pixeladas que aquí busco reproducir y entender.
Idea fundamental: tratar cada luz como si fuera una cámara y capturar una imagen de profundidad de la escena desde la posición de la luz. Esa imagen es un mapa de profundidad o depth map. Al renderizar la escena desde la cámara principal, para cada fragmento transformamos su posición al espacio de la luz y comparamos su profundidad con la almacenada en el mapa. Si la profundidad del mapa es menor que la profundidad del fragmento, algo la oculta y por tanto ese punto está en sombra.
Luces direccionales y cámara ortográfica: las luces direccionales son las más sencillas ya que no tienen perspectiva real; se modelan como una cámara ortográfica con un volumen finito que hay que ajustar. Para construir la matriz vista colocamos la cámara ortográfica suficientemente alejada para cubrir la escena; la proyección se define por los límites del frustum ortográfico y el rango de profundidad. Es importante elegir un up adecuado al calcular lookAt para evitar vectores colineales que produzcan NaN en la matriz vista cuando la dirección apunte exactamente hacia arriba o abajo.
Creación del mapa de sombras: el pipeline de shadow map renderiza la geometría solo escribiendo profundidad, sin iluminación. Hay que enviar las transformaciones de vértices al pipeline y crear una textura de tipo depth con el formato apropiado. Al depurar es útil visualizar el depth map como una imagen en escala de grises, pero hay que recordar que muchas APIs colocan Y igual a cero en la parte superior de la textura, así que puede ser necesario voltear la componente V al muestrear o al proyectar las coordenadas.
Binding y muestreo: las texturas de profundidad no admiten fácilmente capas infinitas, por lo que normalmente se reserva un número máximo de slots para mapas de sombras y se rellenan los vacíos con texturas dummy. El sampler asociado debe ser de comparación para permitir realizar el test de profundidad en la GPU mediante texture compare, por ejemplo comparando si depth en mapa es menor que depth del fragmento. Usar comparador reduce el trabajo en shader y permite aprovechar la GPU para suavizado cuando el sampler es linear.
En el shader la rutina típica consiste en transformar la posición mundial del fragmento por la matriz vista y proyección de la luz, dividir por w para pasar a NDC, convertir de rango negativo-positivo a UV 0-1, y obtener la componente z como la profundidad a comparar. El resultado de texture compare suele ser 1 si no está ocluido y 0 si está en sombra, por lo que se multiplica la contribución de cada luz por ese factor para aplicar la sombra por luz. Para evitar que todas las luces usen el mismo mapa por error hay que asociar cada luz a su mapa de sombras y a un índice que permita seleccionar la textura correcta en el shader.
Gestión de varias luces: en proyectos reales conviene que cada luz lleve su propio conjunto de transformaciones y un indicador de si genera sombras. Al empacar la información de luces se incluyen posición, dirección, color, matrices de vista y proyección de la luz, un flag hasShadow y un índice de sombra. En tiempo de render se generan dos listas: una con la información de todas las luces y otra con las vistas de los mapas de sombras activos, que se enlazan a fixed slots del shader. Si una luz no proyecta sombras se evita la computación innecesaria saltando su cálculo en el shader.
Problemas habituales y soluciones: aparecen artefactos como aliasing y shadow acne. Para mitigarlos se usan varias estrategias combinadas. Aumentar la resolución del mapa mejora la nitidez pero es costoso en memoria. Introducir bias en la comparación de profundidad evita que la propia geometría se autocubra; ese bias se puede aplicar restándolo al depth al muestrear o configurando depth bias y slope scale al crear el pipeline de profundidad, lo que delega el ajuste a la etapa de rasterización. También es útil escalar el bias según el ángulo entre la normal y la dirección de luz para reducir errores en superficies oblicuas. Filtrado lineal del mapa y técnicas avanzadas como PCF o cascaded shadow maps permiten sombras más suaves y escalables para escenas grandes.
Límites y extensiones: las direccionales y los spotlights son relativamente directos; los puntos requieren renderizar seis vistas para un cubemap de profundidad, lo que aumenta el coste. Para escenas complejas existen optimizaciones y variantes como perspective shadow maps o cascaded shadow maps que mejoran la distribución de la resolución del mapa respecto a la cámara. Investigar y aplicar estas técnicas depende del presupuesto de rendimiento y de la Calidad de Imagen requerida.
En mi implementación en WebGPU encontré detalles prácticos importantes: voltear la coordenada V al leer el mapa de profundidad, usar samplers de comparación, evitar confiar en enlazados automáticos cuando se necesita control de filtrado, y empacar correctamente los datos de luz para evitar errores en bindings. El debugging visual del depth map y de la proyección de luz fue clave para detectar problemas sutiles.
Si tu proyecto requiere desarrollo de motores gráficos, integración con servicios cloud o soluciones a medida, en Q2BSTUDIO somos especialistas en desarrollo de software a medida y aplicaciones a medida, y ofrecemos consultoría para integrar renderizado Web y servicios cloud. Podemos ayudar a diseñar pipelines eficientes y a desplegar infraestructura en la nube con soluciones en servicios cloud aws y azure para escalar recursos de render y almacenamiento de mapas de sombras.
Además de desarrollo gráfico ofrecemos servicios de inteligencia artificial, ia para empresas, agentes IA, ciberseguridad y pentesting, y soluciones de inteligencia de negocio y power bi para tomar decisiones basadas en datos. Si buscas crear una aplicación avanzada con componentes de render en tiempo real, IA y despliegue seguro, podemos colaborar en todo el ciclo de desarrollo y operación. Consulta nuestros servicios de desarrollo de aplicaciones y software a medida en desarrollo de aplicaciones multiplataforma y software a medida para ver cómo adaptamos la tecnología a necesidades empresariales concretas.
Resumen y siguientes pasos: con mapas de sombras tienes una solución eficiente para sombras direccionales y spots; para puntos se requiere cubemaps. Tras dominar el flujo básico conviene explorar suavizado por PCF, cascaded shadow maps y ajustes de frustum para optimizar calidad por coste. Si te interesa avanzar con este tipo de técnicas en proyectos reales o integrar capacidades de IA y BI en tu producto, en Q2BSTUDIO ofrecemos experiencia en integración de gráficos, inteligencia artificial, servicios cloud y ciberseguridad para entregarte una solución completa y segura.
Comentarios