Mapas de instantáneas en cascada

Los mapas de sombras en cascada (CSM) son la mejor manera de combatir uno de los errores más frecuentes con sombras: alias 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. Concretamente:

  • 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 aborda algunas de las dificultades comunes asociadas con la adición de filtrado a los CSM; Y
  • muestra cómo asignar CSM a Direct3D 10 a través del hardware de Direct3D 11.

El código usado en este artículo se puede encontrar en los ejemplos kit de desarrollo de software (SDK) de DirectX en los ejemplos CascadedShadowMaps11 y VarianceShadows11. Este artículo resultará más útil después de implementar las técnicas que se tratan en el artículo técnico, Se implementan técnicas comunes para mejorar mapas de profundidad de sombras.

Asignaciones de sombras en cascada y alias de perspectiva

El alias de perspectiva en un mapa de sombras es uno de los problemas más difíciles de solucionar. 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 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 mayor que los 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 representa un mapa de sombras para cada subfrustum; Después, el sombreador de píxeles muestra del mapa que coincide más estrechamente con la resolución necesaria (Figura 2).

Figura 1. Cobertura del mapa de sombras

cobertura de mapa de sombras

En la figura 1, la calidad se muestra (de izquierda a derecha) de mayor a menor. La serie de cuadrículas que representan mapas de sombras con un frustum de vista (cono invertido en rojo) muestra cómo se ve afectada la cobertura de píxeles con diferentes mapas de sombras de resolución. Las sombras son de la mayor calidad (píxeles blancos) cuando hay un píxel de asignación de proporción de 1:1 píxeles en espacio de luz a elementos de textura en el mapa de sombras. El alias de perspectiva se produce en forma de mapas de texturas grandes y en bloques (imagen izquierda) cuando hay demasiados píxeles asignados al mismo elemento de textura de sombra. Cuando el mapa de sombras es demasiado grande, está bajo muestreo. En este caso, se omiten los elementos de textura, se introducen artefactos de shimmering y se ve afectado el rendimiento.

Ilustración 2. Calidad de sombra de CSM

Calidad de sombra csm

En la figura 2 se muestran los recortes de la sección de mayor calidad en 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 ejemplificar 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 de espacio ocular y texturas de mapa de sombras.

Los CSM requieren los pasos siguientes 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 represente.

    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.
      • Muestra la cascada.
      • Ilumina el píxel.

Creación de particiones del frustum

La creación de particiones del frustum es el acto de crear subfrusta. Una técnica para dividir el frustum es calcular los 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. Visualización de frustums particionados arbitrariamente

ver frustums particionados arbitrariamente

En la práctica, recalcular las divisiones de frustum por fotograma hace que los bordes de sombra se mierdan. 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. La determinación de 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 de tierra 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 de tierra, pero los objetos distantes siguen siendo visibles, 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 enlumna en una pequeña sección (como una vista de sobrecarga o un simulador de vuelo) de la vista frustum, se necesitan menos cascadas.

Figura 4. Diferentes configuraciones requieren diferentes divisiones de frustum

diferentes configuraciones requieren diferentes divisiones de frustum

(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 poco beneficio de varios frustums. (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 son casi paralelas, se denomina "frusta de duelo", 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 vuelve paralela con la dirección de la cámara

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

Calcular un límite de View-Frustum

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

Ajustar a la escena

Todo el frusta se puede crear con el mismo plano cercano. Esto obliga a las cascadas a superponerse. El ejemplo CascadedShadowMaps11 llama a esta técnica en la escena.

Ajustar a cascada

Como alternativa, frusta se puede crear con el intervalo de partición real que se usa como planos cercanos y lejanos. Esto hace que un ajuste más ajustado, pero degenerado se ajuste a la escena en el caso de frusta de duelo. Los ejemplos CascadedShadowMaps11 llaman a esta técnica en cascada.

Estos dos métodos se muestran en la figura 6. Ajuste a los desechos en cascada menos resolución. El problema con ajustar a la cascada es que la proyección ortográfica crece y se reduce en función de la orientación del frustum de la vista. La técnica de ajuste a la escena rellena la proyección ortográfica por el tamaño máximo del frustum de la vista quitando los artefactos que aparecen cuando se mueve la cámara de vista. Técnicas comunes para mejorar mapas de profundidad de sombra aborda los artefactos que aparecen cuando la luz se mueve en la sección "Mover la luz en incrementos de tamaño de textura".

Figura 6. Ajustar a la escena frente a ajustar a cascada

ajustar a la escena frente a ajustar a la cascada

Representación del mapa de sombras

El ejemplo CascadedShadowMaps11 representa los mapas de sombras en un 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 abarca la sección del búfer de profundidad correspondiente a esa cascada. Un sombreador de píxeles nulo está enlazado porque solo se necesita la profundidad. Por último, la ventanilla y la matriz de sombras correctas se establecen para cada cascada, ya que los mapas de profundidad se representan de uno en uno en el búfer de sombras principal.

Representación de 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 de Interval-Based

Ilustración 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] );

Después, 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 de Map-Based

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 mapas

selección en cascada basada en mapa

selección de Interval-Based frente a selección de Map-Based

La selección basada en intervalos es ligeramente más rápida que la selección basada en mapas, ya que la selección en cascada se puede realizar directamente. La selección basada en mapa debe intersecr 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 cascadas

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 gui que se puede usar para aumentar y disminuir esta banda de desenfoque. El sombreador realiza una rama dinámica para que la gran mayoría de píxeles solo se lea de la cascada actual.

Figura 9. Costuras en cascada

costuras en cascada

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

Filtrar 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, a continuación, compara esos valores borrosos con el elemento de textura de espacio claro. El borde duro resultante de la prueba de superación o error sigue existiendo. Los mapas de sombras borrosas solo sirven para mover erróneamente el borde duro. PCF permite 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 submuestreos que superan la prueba de profundidad sobre el número total de submuestreos.

El hardware de Direct3D 10 y Direct3D 11 puede realizar PCF. La entrada de un sampler de PCF consta de la coordenada de textura y un valor de profundidad de comparación. Para simplificar, PCF se explica con un filtro de cuatro pulsaciones. El sampler 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 sombra. El valor real devuelto es una interpolación lineal basada en las coordenadas de subtexel de las lecturas de textura para producir 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 por 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 se muestran 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 según los pesos correspondientes del kernel. El ejemplo CascadedShadowMaps11 usa este enfoque.

Sesgo de profundidad

El sesgo de profundidad es aún más importante cuando se usan kernels PCF grandes. Solo es válido comparar la profundidad del espacio claro de un píxel con el píxel al que se asigna en el mapa de profundidad. Los vecinos del mapa de profundidad hacen referencia 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 elementos de textura 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 del espacio de luz de la geometría actual. La solución recomendada a este problema es usar un desplazamiento mayor. Sin embargo, demasiado grande de un desplazamiento puede dar lugar a Peter Panning. El cálculo de un plano cercano ajustado y el plano lejano ayuda a reducir los efectos del uso de un desplazamiento.

Figura 11. Sombra automática errónea

auto sombreado erróneo

Los resultados erróneos de sombreado propio comparan píxeles en la profundidad del espacio claro con los elementos de textura del mapa de sombras que no se correlacionan. La profundidad en el espacio claro se correlaciona con el elemento de textura de sombra 2 en el mapa de profundidad. El elemento de textura 1 es mayor que la profundidad del espacio de luz, mientras que 2 es igual y 3 es menor. Los elementos de textura 2 y 3 superan la prueba de profundidad, mientras que texel 1 produce un error.

Cálculo de un sesgo de profundidad de Per-Texel con DDX y DDY para PCF grandes

Calcular un sesgo por profundidad de textura con ddx y ddy para PCF grandes es una técnica que calcula el sesgo de profundidad correcto, suponiendo que la superficie es planar, para el elemento de textura del mapa de sombras adyacente.

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, puede ser la única técnica que funciona para quitar artefactos de sombreado sin causar Peter Panning.

En la figura 12 se resalta el problema. La profundidad en el espacio de luz es conocida por el elemento de textura que se está comparando. Se desconocen las profundidades de espacio claro que corresponden a los elementos de textura vecinos del mapa de profundidad.

Ilustración 12. Mapa de escena y 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 elemento de textura de espacio ocular se asigna al píxel etiquetado D en el centro del bloque. Esta comparación es precisa. Profundidad correcta en el espacio ocular que se correlaciona con los píxeles que el vecino D es desconocido. La asignación de los elementos de textura vecinos al espacio ocular solo es posible si se supone que el píxel pertenece al mismo triángulo que D.

La profundidad es conocida por el elemento de textura que se correlaciona con la posición del espacio de luz. La profundidad es desconocida para los elementos de textura vecinos en el mapa de profundidad.

En un alto nivel, esta técnica usa las operaciones ddx y HLSL de ddy para encontrar la derivada de la posición del espacio claro. Esto no estrivial porque las operaciones derivadas devuelven el degradado de la profundidad del espacio de luz con respecto al espacio de pantalla. Para convertir esto en un degradado de la profundidad del espacio de luz con respecto al espacio de luz, 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 se asignan las coordenadas de textura de espacio de luz al mapa de profundidad y cómo se pueden usar los derivados de X e Y para crear una matriz de transformación.

Ilustración 13. Matriz de espacio de pantalla a espacio claro

screen-space to light-space matrix (espacio de pantalla a matriz de espacio claro)

Los derivados de la posición de espacio claro 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 estos derivados ejecutando 2 × 2 quad de píxeles en paralelo y restando las coordenadas de textura del vecino en X para ddx y del vecino en Y para ddy. Estos dos derivados componen las filas de una matriz de 2 × 2. En su forma actual, esta matriz se podría usar para convertir píxeles vecinos de espacio de pantalla en pendientes de espacio claro. Sin embargo, se necesita el inverso de esta matriz. Se necesita una matriz que transforma los píxeles vecinos de espacio de luz en pendientes de 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 );

Figura 14. Espacio claro para el espacio de pantalla

espacio claro para el espacio de pantalla

A continuación, esta matriz se usa para transformar los dos elementos de textura arriba y a la derecha del elemento de textura actual. Estos vecinos se representan como un desplazamiento del elemento de textura 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 agregar 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 de Derivative-Based

Agregar los desplazamientos 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 las GPU funcionen. Las GPU de Direct3D11 funcionan en 2 × 2 quads de píxeles. Para realizar un derivado, las GPU suelen restar la copia del píxel actual de una variable de la copia del píxel vecino de esa misma variable. 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 mapas o basada en intervalos. Algunos píxeles de un cuadrante de píxeles eligen una cascada diferente que el resto de los píxeles. Esto da lugar a costuras visibles entre mapas de sombras porque los desplazamientos basados en derivados ahora son completamente incorrectos. La solución consiste en realizar la derivada en coordenadas de textura del espacio de vista ligera. Estas coordenadas son las mismas para cada cascada.

Relleno para kernels de PCF

Los kernels de PCF se indexarán fuera de una partición en cascada si el búfer de sombras no se rellena. La solución consiste en rellenar el borde exterior de la cascada en una mitad del tamaño del kernel PCF. Esto debe implementarse en el sombreador que selecciona la cascada y en la matriz de proyección que debe representar la cascada lo suficientemente grande como para conservar el borde.

Mapas de sombras de varianza

LOS VSM (consulte Mapas de sombras de varianza de Donnelly y Lauritzen para obtener más información) habilitan 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 a través de la convolución. Los VSM tienen algunos inconvenientes; se deben almacenar dos canales de datos de profundidad (profundidad y profundidad al cuadrado). Cuando las sombras se superponen, el sangrado de luz es común. Sin embargo, funcionan bien con resoluciones más bajas y se pueden combinar con los CSM.

Figura 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 desenfocar 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 de profundidad al cuadrado.

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

La comparación de profundidad se realiza.

        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 promedio de cuadrados menos cuadrado de 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 lanzadores de sombra se ocluyen entre sí a lo largo de los bordes. Los VSM sombrea 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.

Figura 16. Sangrado ligero de VSM

sangrado de vsm light

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 altamente optimizadas tienen varias restricciones en la luz, la cámara y la geometría. El sangrado ligero 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 artículo sobre VSM, y autor de un documento sobre LVSM, ha analizado la combinación de mapas de sombra exponenciales (ESM) con VSM para contrarrestar la mezcla de luz en un foro Beyond3D.

VSM con CSM

La varianza de ejemploShadow11 combina VSM y CSM. La combinación es bastante sencilla. El ejemplo sigue los mismos pasos que el ejemplo CascadedShadowMaps11. Dado que PCF no se usa, las sombras se difuminan en una convolución separable de 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, que necesita el filtro. Esto provoca un problema en particular para la selección del mapa mip 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 escalonada a lo largo del mapa de sombras.

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

costuras en bordes en cascada debido al filtrado anisotrópico con control de flujo divergente

Este problema se resuelve calculando los derivados en la posición en el espacio de vista clara; la coordenada del espacio de vista clara 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 al nivel de mapa MIP correcto.

        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 );

VSM comparado con sombras estándar con PCF

Tanto VSM como PCF intentan aproximar la fracción del área de píxeles que superaría la prueba de profundidad. Los VSM funcionan con hardware de filtrado y se pueden desenfocar con kernels 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 del espacio claro. Esto significa que los VSM no tienen los mismos problemas de desplazamiento que PCF. Técnicamente, los VSM son la profundidad de muestreo en un área mayor, así como la realización de 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, lo que hace que sea necesario menos desplazamiento. Como se ha descrito anteriormente, el inconveniente número uno de los VSM es un sangrado ligero.

Los 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 por 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 menor resolución debido a la combinación y el filtrado.

Resumen

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

Referencias

Donnelly, W. y Lauritzen, 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.