Compartir a través de


Mapas de sombras en cascada

Los mapas de sombras en cascada (CSM) son la mejor manera de combatir uno de los errores más frecuentes del sombreado: el aliasing de perspectiva. En este artículo técnico, que supone que el lector está familiarizado con la asignación de sombras, aborda el tema de los CSM. En concreto, lo siguiente:

  • explica la complejidad de los CSM;
  • proporciona detalles sobre las posibles variaciones de los algoritmos CSM;
  • describe las dos técnicas de filtrado más comunes: porcentaje de filtrado más cercano (PCF) y filtrado con mapas de sombras de varianza (VSM);
  • identifica y soluciona algunos de los problemas comunes asociados con la adición de filtrado a los CSM; y
  • muestra cómo mapear CSMs a Direct3D 10 en hardware de Direct3D 11.

El código usado en este artículo se puede encontrar en los ejemplos del Kit de desarrollo de software (SDK) de DirectX en los ejemplos CascadedShadowMaps11 y VarianceShadows11. Este artículo resultará más útil una vez que se hayan implementado las técnicas descritas en el artículo técnico, Técnicas comunes para mejorar mapas de profundidad de sombras.

Mapas de sombras en cascada y aliasing de perspectiva

El aliasing de perspectiva en un mapa de sombras es uno de los problemas más difíciles de superar. En el artículo técnico, Técnicas comunes para mejorar mapas de profundidad de sombras, se describe el alias de perspectiva y se identifican algunos enfoques para mitigar el problema. En la práctica, los CSM tienden a ser la mejor solución y suelen emplearse en juegos modernos.

El concepto básico de los CSM es fácil de entender. Las diferentes áreas del frustum de la cámara requieren mapas de sombras con diferentes resoluciones. Los objetos más cercanos al ojo requieren una resolución más alta que objetos más lejanos. De hecho, cuando el ojo se mueve muy cerca de la geometría, los píxeles más cercanos al ojo pueden requerir tanta resolución que incluso un mapa de sombras 4096 × 4096 no es suficiente.

La idea básica de los CSM es dividir el frustum en varios frusta. Se renderiza un mapa de sombras para cada subfrustum; después, el sombreador de píxeles muestrea del mapa que coincide más de cerca con la resolución necesaria (Figura 2).

Figura 1. Cobertura de mapa de sombras

cobertura de mapa de sombras

En la figura 1, la calidad se muestra (de izquierda a derecha) de la más alta a la más baja. La serie de cuadrículas que representan mapas de sombras con un cono de visión (cono invertido en rojo) muestra cómo se ve afectada la cobertura de píxeles con mapas de sombras de diferentes resoluciones. Las sombras son de la mayor calidad (píxeles blancos) cuando hay una correspondencia de 1:1 entre los píxeles en el espacio de vista y los texels en el mapa de sombras. El aliasing de perspectiva se produce en forma de mapas de texturas en grandes bloques (imagen izquierda) cuando demasiados píxeles se asignan al mismo texel de sombra. Cuando el mapa de sombras es demasiado grande, está submuestreado. En este caso, se omiten los texeles, se introducen artefactos de parpadeo y el rendimiento se ve afectado.

Ilustración 2. Calidad de sombra de CSM

CSM Shadow Quality

En la figura 2 se muestran los recortes de la sección de mayor calidad de cada mapa de sombras de la figura 1. El mapa de sombras con los píxeles más estrechamente colocados (en el vértice) es el más cercano al ojo. Técnicamente, estos son mapas del mismo tamaño, con blanco y gris usado para ilustrar el éxito del mapa de sombras en cascada. El blanco es ideal porque muestra una buena cobertura: una relación de 1:1 para píxeles del espacio visual y texeles de mapas de sombras.

Los CSM requieren los siguientes pasos por fotograma.

  1. Particione el frustum en subfrusta.

  2. Calcule una proyección ortográfica para cada subfrustum.

  3. Representar un mapa de sombras para cada subfrustum.

  4. Representar la escena.

    1. Enlace los mapas de sombras y renderice.

    2. El sombreador de vértices hace lo siguiente:

      • Calcula las coordenadas de textura para cada subfrustum claro (a menos que la coordenada de textura necesaria se calcule en el sombreador de píxeles).
      • Transforma y ilumina el vértice, etc.
    3. El sombreador de píxeles hace lo siguiente:

      • Determina el mapa de sombras adecuado.
      • Transforma las coordenadas de textura si es necesario.
      • Muestrea la cascada.
      • Ilumina el píxel.

Partición del Frustum

La partición del frustum es el acto de crear subfrustums. Una técnica para dividir el frustum es calcular intervalos de cero por ciento a cien por ciento en la dirección Z. A continuación, cada intervalo representa un plano cercano y un plano lejano como porcentaje del eje Z.

Figura 3. Vista de frustums particionados arbitrariamente

ver frustums particionados arbitrariamente

En la práctica, recalcular las divisiones del frustum por fotograma hace que los bordes de las sombras titilen. La práctica generalmente aceptada es usar un conjunto estático de intervalos en cascada por escenario. En este escenario, el intervalo a lo largo del eje Z se usa para describir un subfrustum que se produce al particionar el frustum. Determinar los intervalos de tamaño correctos para una escena determinada depende de varios factores.

Orientación de la geometría de la escena

Con respecto a la geometría de la escena, la orientación de la cámara afecta a la selección del intervalo en cascada. Por ejemplo, una cámara muy cerca del suelo, como una cámara terrestre en un partido de fútbol, tiene un conjunto estático diferente de intervalos en cascada que una cámara en el cielo.

En la figura 4 se muestran algunas cámaras diferentes y sus respectivas particiones. Cuando el rango Z de la escena es muy grande, se requieren más planos divididos. Por ejemplo, cuando el ojo está muy cerca del plano terrestre, pero los objetos distantes siguen siendo visibles, se pueden ser necesarias varias cascadas. Dividir el frustum para que más divisiones estén cerca del ojo (donde el alias de perspectiva cambia más rápido) también es valioso. Cuando la mayoría de la geometría se agrupa en una pequeña sección (como una vista cenital o un simulador de vuelo) del frustum de visión, se requieren menos cascadas.

Figura 4. Diferentes configuraciones requieren diferentes particiones de frustum

diferentes configuraciones requieren diferentes divisiones frustrum

(Izquierda) Cuando la geometría tiene un rango dinámico alto en Z, se requieren muchas cascadas. (Centro) Cuando la geometría tiene un rango dinámico bajo en Z, hay poca ventaja de usar múltiples frustums (troncos de pirámide). (Derecha) Solo se necesitan tres particiones cuando el intervalo dinámico es medio.

Orientación de la luz y la cámara

La matriz de proyección de cada cascada se ajusta estrechamente alrededor de su subfrustum correspondiente. En configuraciones en las que la cámara de vista y las direcciones de luz son ortogonales, las cascadas pueden ajustarse estrechamente con poca superposición. La superposición se vuelve más grande a medida que la luz y la cámara de vista se mueven a la alineación paralela (Figura 5). Cuando la luz y la cámara de vista están casi opuestas (orientadas entre sí), se denomina "dueling frusta", y es un escenario muy difícil para la mayoría de los algoritmos de sombra. No es raro restringir la luz y la cámara para que este escenario no se produzca. Sin embargo, los CSM funcionan mucho mejor que muchos otros algoritmos en este escenario.

Figura 5. La superposición en cascada aumenta a medida que la dirección de la luz se vuelve paralela con la dirección de la cámara

la superposición en cascada aumenta a medida que la dirección de la luz se convierte en paralelo con la dirección de la cámara

Muchas implementaciones de CSM utilizan frustas de tamaño fijo. El sombreador de píxeles puede usar la profundidad Z para acceder a la matriz de cascadas cuando el frustum se divide en intervalos de tamaño fijo.

Cálculo de un límite del View-Frustum

Una vez seleccionados los intervalos de frustum, la subfrusta se crea con una de las dos opciones: ajustarla a la escena y ajustarla a la cascada.

Ajustar a la escena

Todos los frusta se pueden crear con el mismo plano cercano. Esto obliga a las cascadas a superponerse. El ejemplo CascadedShadowMaps11 llama a esta técnica "adecuada para la escena".

Ajustar a cascada

Como alternativa, se pueden crear frustas con el intervalo de partición real que se usa como planos cercano y lejano. Esto provoca un ajuste más preciso, pero degenera en un ajuste a la escena en el caso de frustum de duelo. Los ejemplos CascadedShadowMaps11 llaman a esta técnica en cascada.

Estos dos métodos se muestran en la figura 6. Ajustar en cascada minimiza el desperdicio de resolución. El problema con el ajuste a la cascada es que la proyección ortográfica crece y se reduce según la orientación del frustum de vista. La técnica de ajuste a la escena amplía la proyección ortográfica al tamaño máximo del cono de visión, eliminando los artefactos que aparecen cuando la cámara se mueve. Las técnicas comunes para mejorar los mapas de profundidad de sombra abordan los artefactos que aparecen cuando la luz se mueve en la sección "Mover la luz en incrementos del tamaño de un texel".

Figura 6. Ajustar a la escena frente a ajustar al flujo

ajustar a la escena frente a ajustar a la cascada

Representar el mapa de sombras

El ejemplo CascadedShadowMaps11 representa los mapas de sombras en un único búfer grande. Esto se debe a que PCF en matrices de texturas es una característica de Direct3D 10.1. Para cada cascada, se crea una ventanilla que cubre la sección del búfer de profundidad correspondiente a esa cascada. Se enlaza un sombreador de píxeles nulo porque se necesita solo la profundidad. Por último, los viewports y la matriz de sombras correctas se establecen para cada cascada, mientras que los mapas de profundidad se renderizan uno por uno en el búfer de sombras principal.

Representar la escena

El búfer que contiene las sombras ahora está enlazado al sombreador de píxeles. Hay dos métodos para seleccionar la cascada implementada en el ejemplo CascadedShadowMaps11. Estos dos métodos se explican con código de sombreador.

Selección en cascada basada en intervalos

Figura 7. Selección en cascada basada en intervalos

selección en cascada basada en intervalos

En la selección basada en intervalos (figura 7), el sombreador de vértices calcula la posición en el espacio mundial del vértice.

Output.vDepth = mul( Input.vPosition, m_mWorldView ).z;

El sombreador de píxeles recibe la profundidad interpolada.

fCurrentPixelDepth = Input.vDepth;

La selección en cascada basada en intervalos usa una comparación de vectores y un producto de puntos para determinar el cacade correcto. El CASCADE_COUNT_FLAG especifica el número de cascadas. El m_fCascadeFrustumsEyeSpaceDepths_data restringe las particiones de frustum de vista. Después de la comparación, fComparison contiene un valor de 1 donde el píxel actual es mayor que la barrera y un valor de 0 cuando la cascada actual es menor. Un producto de punto suma estos valores en un índice de matriz.

        float4 vCurrentPixelDepth = Input.vDepth;
        float4 fComparison = ( vCurrentPixelDepth > m_fCascadeFrustumsEyeSpaceDepths_data[0]);
        float fIndex = dot(
        float4( CASCADE_COUNT_FLAG > 0,
        CASCADE_COUNT_FLAG > 1,
        CASCADE_COUNT_FLAG > 2,
        CASCADE_COUNT_FLAG > 3)
        , fComparison );

        fIndex = min( fIndex, CASCADE_COUNT_FLAG );
        iCurrentCascadeIndex = (int)fIndex;

Una vez seleccionada la cascada, la coordenada de textura debe transformarse en la cascada correcta.

vShadowTexCoord = mul( InterpolatedPosition, m_mShadow[iCascadeIndex] );

A continuación, esta coordenada de textura se usa para muestrear la textura con la coordenada X y la coordenada Y. La coordenada Z se usa para realizar la comparación de profundidad final.

Selección en Cascada Basada en Mapas

La selección basada en mapas (Figura 8) comprueba los cuatro lados de las cascadas para encontrar el mapa más estrecho que cubre el píxel específico. En lugar de calcular la posición en el espacio mundial, el sombreador de vértices calcula la posición del espacio de vista para cada cascada. El sombreador de píxeles recorre en iteración las cascadas para escalar y desplazar las coordenadas de textura para que indexen la cascada actual. A continuación, la coordenada de textura se prueba con los límites de textura. Cuando los valores X e Y de la coordenada de textura se encuentran dentro de una cascada, se usan para muestrear la textura. La coordenada Z se usa para realizar la comparación de profundidad final.

Figura 8. Selección en cascada basada en mapa

selección en cascada basada en mapa

Selección basada en intervalos frente a selección basada en mapas

La selección basada en intervalos es ligeramente más rápida que la selección basada en mapas porque la selección en cascada se puede realizar directamente. La selección basada en mapas debe intersectar la coordenada de textura con los límites en cascada.

La selección basada en mapas usa la cascada de forma más eficaz cuando los mapas de sombras no se alinean perfectamente (vea la figura 8).

Fusión entre Cascades

Los VSM (descritos más adelante en este artículo) y las técnicas de filtrado, como PCF, se pueden usar con CSM de baja resolución para producir sombras suaves. Desafortunadamente, esto da como resultado una costura visible (figura 9) entre capas en cascada porque la resolución no coincide. La solución consiste en crear una banda entre mapas de sombras donde se realiza la prueba de sombras para ambas cascadas. Después, el sombreador interpola linealmente entre los dos valores en función de la ubicación del píxel en la banda de mezcla. Los ejemplos CascadedShadowMaps11 y VarianceShadows11 proporcionan un control deslizante en la interfaz gráfica de usuario que se puede usar para aumentar y disminuir este nivel de desenfoque. El sombreador realiza una bifurcación dinámica para que la gran mayoría de píxeles solo lea de la cascada actual.

Figura 9. Costuras en cascada

costuras en cascada

(Izquierda) Se puede ver una costura visible donde las cascadas se superponen. (Derecha) Cuando las cascadas se mezclan entre ellas, no se produce ninguna costura.

Filtrado de mapas de sombras

PCF

El filtrado de mapas de sombras normales no produce sombras suaves y borrosas. El hardware de filtrado desenfoca los valores de profundidad y luego compara esos valores borrosos con el texel del espacio de luz. El límite estricto resultante de la prueba de superación/error sigue existiendo. Los mapas de sombras borrosas solo sirven para desplazar incorrectamente el borde duro. PCF habilita el filtrado en mapas de sombras. La idea general de PCF es calcular un porcentaje del píxel en sombra en función del número de submuestras que superen la prueba de profundidad sobre el número total de submuestreos.

El hardware Direct3D 10 y Direct3D 11 puede ejecutar PCF. La entrada de un muestreador de PCF consta de la coordenada de textura y un valor de profundidad de comparación. Para simplificar, el PCF se explica con un filtro de cuatro toques. El muestreador de textura lee la textura cuatro veces, similar a un filtro estándar. Sin embargo, el resultado devuelto es un porcentaje de los píxeles que han superado la prueba de profundidad. En la figura 10 se muestra cómo un píxel que supera una de las cuatro pruebas de profundidad es del 25 por ciento en la sombra. El valor real devuelto es una interpolación lineal basada en las coordenadas de subtexel de las lecturas de textura para generar un degradado suave. Sin esta interpolación lineal, el PCF de cuatro pulsaciones solo podría devolver cinco valores: { 0.0, 0.25, 0.5, 0.75, 1.0 }.

Figura 10. Imagen filtrada por PCF, con el 25 % del píxel seleccionado cubierto

imagen filtrada pcf, con el 25 % del píxel seleccionado cubierto

También es posible realizar PCF sin soporte de hardware o extender PCF a kernels más grandes. Algunas técnicas incluso muestrean con un kernel ponderado. Para ello, cree un kernel (como un gaussiano) para una cuadrícula N × N. Los pesos deben sumar hasta 1. A continuación, la textura se muestrea N2 veces. Cada muestra se escala por los pesos correspondientes en el kernel. El ejemplo CascadedShadowMaps11 usa este enfoque.

Sesgo de profundidad

El sesgo de profundidad se vuelve aún más importante cuando se usan kernels PCF grandes. Solo es válido comparar la profundidad del espacio de luz de un píxel con el píxel al que se asigna en el mapa de profundidad. Los vecinos del texel del mapa de profundidad se refieren a una posición diferente. Es probable que esta profundidad sea similar, pero puede ser muy diferente en función de la escena. En la figura 11 se resaltan los artefactos que se producen. Una sola profundidad se compara con tres texeles vecinos en el mapa de sombras. Una de las pruebas de profundidad falla erróneamente porque su profundidad no se correlaciona con la profundidad calculada en el espacio de luz de la geometría actual. La solución recomendada para este problema es usar un desplazamiento mayor. Sin embargo, un desplazamiento demasiado grande puede dar lugar a un fenómeno llamado Peter Panning. Calcular un plano de recorte cercano y un plano de recorte lejano ajustados ayuda a reducir los efectos de usar un desplazamiento.

Figura 11. Sombras propias erróneas

autoproyección de sombras errónea

Los errores en el sombreado propio resultan de comparar píxeles en la profundidad en el espacio lumínico con texeles en el mapa de sombras que no se corresponden. La profundidad en el espacio luminoso se correlaciona con el texel de sombreado 2 en el mapa de profundidad. El texel 1 es mayor que la profundidad del espacio de luz, mientras que 2 es igual y 3 es menor. Texels 2 y 3 superan la prueba de profundidad, mientras que el Texel 1 no la pasa.

Cálculo de un sesgo de profundidad por texel con DDX y DDY para PCF grandes

Calcular un sesgo de profundidad por texel con ddx y ddy para grandes PCFs es una técnica que calcula el sesgo de profundidad correcto, suponiendo que la superficie sea plana, para el texel adyacente del mapa de sombras.

Esta técnica ajusta la profundidad de comparación a un plano mediante la información derivada. Dado que esta técnica es computacionalmente compleja, solo se debe usar cuando una GPU tiene ciclos de proceso para ahorrar. Cuando se usan kernels muy grandes, esta puede ser la única técnica que funciona para quitar artefactos de auto-sombreado sin causar efectos de "Peter Panning".

En la figura 12 se resalta el problema. La profundidad en el espacio de luz se conoce por el texel que se está comparando. Las profundidades de espacio claro que corresponden a los elementos de textura vecinos del mapa de profundidad son desconocidos.

Figura 12. Escena y mapa de profundidad

mapa de escena y profundidad

La escena representada se muestra a la izquierda y el mapa de profundidad con un bloque de textura de ejemplo se muestra a la derecha. El texel del espacio ocular se mapea con el píxel D ubicado en el centro del bloque. Esta comparación es precisa. La profundidad correcta en el espacio ocular que se correlaciona con los píxeles vecinos a D, es desconocida. La asignación de texels vecinos al espacio de visión solo es posible si se supone que el píxel pertenece al mismo triángulo que D.

La profundidad es conocida por el texel que correlaciona con la posición en el espacio de luz. La profundidad de los texeles vecinos es desconocida en el mapa de profundidad.

En un nivel alto, esta técnica usa las operaciones ddx y ddy HLSL para encontrar la derivada de la posición de espacio ligero. Esto no es trivial porque las operaciones de derivada devuelven el gradiente de la profundidad del espacio de luz con respecto al espacio de pantalla. Para convertir esto en un gradiente de la profundidad en el espacio de luz con respecto a dicho espacio, se debe calcular una matriz de conversión.

Explicación con código de sombreador

Los detalles del resto del algoritmo se proporcionan como explicación del código del sombreador que realiza esta operación. Este código se puede encontrar en el ejemplo CascadedShadowMaps11. En la figura 13 se muestra cómo las coordenadas de textura de espacio claro se asignan al mapa de profundidad y cómo se pueden usar los derivados de X e Y para crear una matriz de transformación.

Figura 13. Matriz de espacio de pantalla a espacio claro

matriz de espacio de pantalla a espacio de luz

Las derivadas de la posición en el espacio de luz en X e Y se usan para crear esta matriz.

El primer paso es calcular la derivada de la posición del espacio de vista claro.

          float3 vShadowTexDDX = ddx (vShadowMapTextureCoordViewSpace);
          float3 vShadowTexDDY = ddy (vShadowMapTextureCoordViewSpace);

Las GPU de clase Direct3D 11 calculan estas derivadas ejecutando un quad de 2 × 2 píxeles en paralelo y restando las coordenadas de textura del vecino en el eje X para ddx y del vecino en el eje Y para ddy. Estos dos derivados componen las filas de una matriz de 2 × 2. En su forma actual, esta matriz podría usarse para convertir los píxeles vecinos del espacio de pantalla a inclinaciones del espacio luminoso. Sin embargo, se necesita el inverso de esta matriz. Se necesita una matriz que transforme los píxeles vecinos del espacio de luz en pendientes del espacio de pantalla.

          float2x2 matScreentoShadow = float2x2( vShadowTexDDX.xy, vShadowTexDDY.xy );
          float fInvDeterminant = 1.0f / fDeterminant;

          float2x2 matShadowToScreen = float2x2 (
          matScreentoShadow._22 * fInvDeterminant,
          matScreentoShadow._12 * -fInvDeterminant,
          matScreentoShadow._21 * -fInvDeterminant,
          matScreentoShadow._11 * fInvDeterminant );

Ilustración 14. Espacio de luz a espacio de pantalla

espacio de luz en el espacio de pantalla

Esta matriz se usa para transformar los dos texeles que se encuentran encima y a la derecha del texel actual. Estos vecinos se representan como un desplazamiento desde el texel actual.

          float2 vRightShadowTexelLocation = float2( m_fTexelSize, 0.0f );
          float2 vUpShadowTexelLocation = float2( 0.0f, m_fTexelSize );
          float2 vRightTexelDepthRatio = mul( vRightShadowTexelLocation,
          matShadowToScreen );
          float2 vUpTexelDepthRatio = mul( vUpShadowTexelLocation,
          matShadowToScreen );

La relación que crea la matriz se multiplica finalmente por los derivados de profundidad para calcular los desplazamientos de profundidad de los píxeles vecinos.

            float fUpTexelDepthDelta =
            vUpTexelDepthRatio.x * vShadowTexDDX.z
            + vUpTexelDepthRatio.y * vShadowTexDDY.z;
            float fRightTexelDepthDelta =
            vRightTexelDepthRatio.x * vShadowTexDDX.z
            + vRightTexelDepthRatio.y * vShadowTexDDY.z;

Estos pesos ahora se pueden usar en un bucle PCF para aplicar un desplazamiento a la posición.

    for( int x = m_iPCFBlurForLoopStart; x < m_iPCFBlurForLoopEnd; ++x ) 
    {
        for( int y = m_iPCFBlurForLoopStart; y < m_iPCFBlurForLoopEnd; ++y )
            {
            if ( USE_DERIVATIVES_FOR_DEPTH_OFFSET_FLAG )
            {
            depthcompare += fRightTexelDepthDelta * ( (float) x ) +
            fUpTexelDepthDelta * ( (float) y );
            }
            // Compare the transformed pixel depth to the depth read
            // from the map.
            fPercentLit += g_txShadow.SampleCmpLevelZero( g_samShadow,
            float2(
            vShadowTexCoord.x + ( ( (float) x ) * m_fNativeTexelSizeInX ) ,
            vShadowTexCoord.y + ( ( (float) y ) * m_fTexelSize )
            ),
            depthcompare
            );
            }
     }

PCF y CSM

PCF no funciona en matrices de texturas en Direct3D 10. Para usar PCF, todas las cascadas se almacenan en un atlas de textura grande.

desplazamiento basado en derivadas

Agregar los offsets basados en derivados para los CSM presenta algunos desafíos. Esto se debe a un cálculo derivado dentro del control de flujo divergente. El problema se produce debido a una manera fundamental de que funcionan las GPU. Las GPU de Direct3D11 funcionan en cuadrículas de 2 × 2 píxeles. Para calcular una derivada, las GPU suelen restar la copia de una variable del píxel actual de la copia de esa misma variable del píxel vecino. El modo en que esto varía de GPU a GPU. Las coordenadas de textura se determinan mediante la selección en cascada basada en mapa o basada en intervalos. Algunos píxeles de una cuatrícula de píxeles eligen una cascada diferente al resto. Esto da como resultado costuras visibles entre mapas de sombras porque los desplazamientos basados en derivados ahora son completamente incorrectos. La solución consiste en calcular la derivada en las coordenadas de textura del espacio de vista de luz. Estas coordenadas son las mismas para cada cascada.

Relleno para kernels de PCF

Índice de kernels de PCF fuera de una partición en cascada si el búfer de sombras no está rellenado. La solución consiste en rellenar el borde exterior de la cascada en una mitad del tamaño del kernel PCF. Debe implementarse en el sombreador que seleccione la cascada y en la matriz de proyección que debe representar la cascada lo suficientemente grande como para que se conserve el borde.

Mapas de varianza de sombras

Los VSM (consulte Mapas de sombras de varianza de Donnelly y Lauritzen para obtener más información) permiten el filtrado directo del mapa de sombras. Al usar VSM, se puede usar toda la potencia del hardware de filtrado de texturas. Se puede usar el filtrado trilineal y anisotrópico (figura 15). Además, los VSM se pueden desenfocar directamente mediante la convolución. Los VSM tienen algunas desventajas; se deben almacenar dos canales de datos de profundidad (profundidad y profundidad al cuadrado). Cuando las sombras se superponen, las fugas de luz son comunes. Sin embargo, funcionan bien, con resoluciones más bajas y se pueden combinar con los CSM.

Ilustración 15. Filtrado anisotrópico

filtrado anisotrópico

Detalles del algoritmo

Los VSM funcionan mediante la representación de la profundidad y la profundidad al cuadrado en un mapa de sombras de dos canales. Este mapa de sombras de dos canales se puede borrar y filtrar como una textura normal. A continuación, el algoritmo usa la desigualdad de Chebychev en el sombreador de píxeles para calcular la fracción del área de píxeles que superaría la prueba de profundidad.

El sombreador de píxeles captura los valores de profundidad y cuadrado de profundidad.

        float  fAvgZ  = mapDepth.x; // Filtered z
        float  fAvgZ2 = mapDepth.y; // Filtered z-squared

Se realiza la comparación de profundidad.

        if ( fDepth <= fAvgZ )
        {
        fPercentLit = 1;
        }

Si se produce un error en la comparación de profundidad, se estima el porcentaje del píxel que se ilumina. La varianza se calcula como la media de los cuadrados menos el cuadrado de la media.

        float variance = ( fAvgZ2 ) − ( fAvgZ * fAvgZ );
        variance = min( 1.0f, max( 0.0f, variance + 0.00001f ) );

El valor fPercentLit se calcula con la desigualdad de Chebychev.

        float mean           = fAvgZ;
        float d              = fDepth - mean;
        float fPercentLit    = variance / ( variance + d*d );

Sangrado ligero

El mayor inconveniente de los VSM es el sangrado ligero (figura 16). El sangrado de luz se produce cuando varios emisores de sombra se bloquean mutuamente a lo largo de los bordes. Los VSM sombrean los bordes de las sombras en función de las disparidades de profundidad. Cuando las sombras se superponen entre sí, existe una disparidad de profundidad en el centro de una región que se debe sombrear. Se trata de un problema con el uso del algoritmo VSM.

Ilustración 16. Sangrado ligero de VSM

sangrado de la luz VSM

Una solución parcial al problema es elevar fPercentLit a una potencia. Esto tiene el efecto de amortiguar el desenfoque, lo que puede provocar artefactos donde la disparidad de profundidad es pequeña. A veces existe un valor mágico que alivia el problema.

fPercentLit = pow( p_max, MAGIC_NUMBER );

Una alternativa a elevar el porcentaje iluminado a una potencia es evitar configuraciones en las que las sombras se superponen. Incluso las configuraciones de sombra muy ajustadas tienen varias restricciones en la luz, la cámara y la geometría. El sangrado de luz también se reduce mediante el uso de texturas de mayor resolución.

Los mapas de sombras de varianza superpuestas (LVSM) resuelven el problema a costa de dividir el frustum en capas que son perpendiculares a la luz. El número de mapas necesarios sería bastante grande cuando también se usan los CSM.

Además, Andrew Lauritzen, coautor del documento sobre VSM y autor de un documento sobre LVSM, discutió en un Foro Beyond3D la combinación de mapas exponenciales de sombras (ESM) con VSM para contrarrestar la combinación de luz.

VSM con CSM

La muestra VarianceShadow11 combina VSMs y CSMs. La combinación es bastante sencilla. El ejemplo sigue los mismos pasos que el ejemplo CascadedShadowMaps11. Dado que no se utiliza PCF, las sombras se difuminan mediante una convolución separable en dos pasos. No usar PCF también permite que el ejemplo use matrices de texturas en lugar de un atlas de texturas. PCF en matrices de texturas es una característica de Direct3D 10.1.

Degradados con CSM

El uso de degradados con CSM puede producir una costura a lo largo del borde entre dos cascadas, como se muestra en la figura 17. La instrucción de ejemplo usa derivados entre píxeles para calcular información, como el nivel de mapa mip, necesario para el filtro. Esto causa un problema en particular para la selección de mipmap o el filtrado anisotrópico. Cuando los píxeles de un quad toman ramas diferentes en el sombreador, los derivados calculados por el hardware de GPU no son válidos. Esto da como resultado una costura dentada a lo largo del mapa de sombras.

Ilustración 17. Costuras en bordes en cascada debido al filtrado anisotrópico con control de flujo divergente

costuras en límites de cascada debido al filtrado anisotrópico con control de flujo divergente

Este problema se resuelve calculando las derivadas en la posición en el espacio de vista de luz; la coordenada en el espacio de vista de luz no es específica de la cascada seleccionada. Los derivados calculados se pueden escalar mediante la parte de escala de la matriz de textura de proyección para alcanzar el nivel correcto de mapa mip.

        float3 vShadowTexCoordDDX = ddx( vShadowMapTextureCoordViewSpace );
        vShadowTexCoordDDX *= m_vCascadeScale[iCascade].xyz;
        float3 vShadowTexCoordDDY = ddy( vShadowMapTextureCoordViewSpace );
        vShadowTexCoordDDY *= m_vCascadeScale[iCascade].xyz;

        mapDepth += g_txShadow.SampleGrad( g_samShadow, vShadowTexCoord.xyz,
        vShadowTexCoordDDX, vShadowTexCoordDDY );

Comparación de VSM con sombras estándar y PCF

Tanto VSM como PCF intentan aproximar la fracción del área de píxeles que superaría la prueba de profundidad. Los VSM trabajan con hardware de filtrado y se pueden desenfocar con núcleos separables. Los kernels de convolución separables son considerablemente más baratos que un kernel completo. Además, los VSM comparan una profundidad de espacio claro con un valor en el mapa de profundidad de espacio claro. Esto significa que los VSM no tienen los mismos problemas de desplazamiento que PCF. Técnicamente, los VSM están realizando un muestreo de profundidad en un área mayor, así como un análisis estadístico. Esto es menos preciso que PCF. En la práctica, los VSM realizan un trabajo muy bueno de fusión, resultando en que se necesite menos desfase. Como se ha descrito anteriormente, el inconveniente número uno de los VSM es un sangrado ligero.

VSM y PCF representan un equilibrio entre la potencia de proceso de GPU y el ancho de banda de textura de GPU. Los VSM requieren que se realicen más cálculos matemáticos para calcular la varianza. PCF requiere más ancho de banda de memoria de textura. Los kernels PCF grandes pueden convertirse rápidamente en cuellos de botella debido al ancho de banda de textura. Con la potencia de cálculo de GPU creciendo más rápidamente que el ancho de banda de GPU, los VSM se están convirtiendo en el más práctico de los dos algoritmos. Los VSM también se ven mejor con mapas de sombras de resolución inferior debido a la combinación y el filtrado.

Resumen

Los CSM ofrecen una solución al problema de aliasing de perspectiva. Hay varias configuraciones posibles para obtener la fidelidad visual necesaria para un título. PCF y VSMs se usan ampliamente y deben combinarse con los CSMs para reducir el aliasing.

Referencias

Donnelly, W. y Lauritzen, A. Mapas de sombras de varianza. En si3D '06: Actas del simposio de 2006 sobre gráficos y juegos interactivos 3D. 2006. pp. 161–165. Nueva York, NY, EE. UU.: ACM Press.

Lauritzen, Andrew y McCool, Michael. Mapas de sombras de varianza superpuestas. Actas de la interfaz gráfica 2008, 28 de mayo de 2008, Windsor, Ontario, Canadá.

Engel, Woflgang F. Sección 4. Mapas de sombras en cascada. ShaderX5 , Advanced Rendering Techniques, Wolfgang F. Engel, Ed. Charles River Media, Boston, Massachusetts. 2006. pp. 197–206.