级联阴影映射

级联阴影映射(CSM)是对抗最普遍的错误之一的最佳方法:透视别名。 此技术文章假定读者熟悉阴影映射,可解决 CSM 的主题。 具体而言,它:

  • 解释 CSM 的复杂性;
  • 提供有关 CSM 算法可能变体的详细信息;
  • 介绍两种最常见的筛选技术-百分比更接近筛选(PCF)和使用方差阴影映射进行筛选(VSM):
  • 标识并解决与向 CSM 添加筛选相关的一些常见陷阱;和
  • 演示如何将 CSM 映射到 Direct3D 10 到 Direct3D 11 硬件。

本文中使用的代码可以在 CascadedShadowMaps11 和 VarianceShadows11 示例中的 DirectX 软件开发工具包(SDK)中找到。 本文将在实施技术文章中所述的技术后证明最有用,改进阴影深度地图的常见技术。

级联阴影映射和透视别名

阴影图中的透视别名是要克服的最困难问题之一。 在技术文章中,介绍了改进阴影深度地图的常见技术、透视别名以及一些缓解问题的方法。 在实践中,CSM 通常是最佳解决方案,通常用于现代游戏。

CSM 的基本概念易于理解。 相机视线的不同区域需要具有不同分辨率的阴影映射。 距离眼睛最近的物体需要比更远的对象更高的分辨率。 事实上,当眼睛移动非常接近几何图形时,最接近眼睛的像素可能需要这么多分辨率,即使 4096 × 4096 阴影地图也不足。

CSM 的基本理念是将 frustum 分区为多个 frusta。 为每个子图形呈现阴影映射;然后,像素着色器从地图中采样最符合所需分辨率(图 2)。

图 1. 阴影地图覆盖率

阴影地图覆盖率

在图 1 中,质量从高到低显示(从左到右)。 一系列网格表示阴影贴图(红色反圆锥图)显示像素覆盖率如何受到不同的分辨率阴影映射的影响。 当浅色空间中的 1:1 比率映射像素与阴影映射中的纹素时,阴影是质量最高的(白色像素)。 当像素过多映射到同一阴影纹素时,透视别名以大块纹理映射(左图像)的形式出现。 当阴影映射太大时,它处于采样状态。 在这种情况下,跳过纹素,引入闪闪发光的项目,并影响性能。

图 2. CSM 阴影质量

csm 阴影质量

图 2 显示了图 1 中每个阴影图中质量最高的部分的切口。 最接近放置像素(顶点)的阴影地图最接近眼睛。 从技术上说,这些地图的大小相同,白色和灰色用于演示级联阴影地图的成功。 白色是理想的,因为它显示良好的覆盖范围 - 1:1 的眼距像素和阴影地图纹素的比例。

CSM 要求每个帧执行以下步骤。

  1. 将 frustum 分区为子frusta。

  2. 为每个子图形计算一个正交投影。

  3. 呈现每个子图形的阴影映射。

  4. 呈现场景。

    1. 绑定阴影映射和呈现。

    2. 顶点着色器执行以下作:

      • 计算每个光子图形的纹理坐标(除非在像素着色器中计算所需的纹理坐标)。
      • 转换和点亮顶点等。
    3. 像素着色器执行以下作:

      • 确定正确的阴影映射。
      • 根据需要转换纹理坐标。
      • 对级联采样。
      • 照亮像素。

分区 Frustum

对 frustum 进行分区是创建子frusta 的行为。 拆分 frustum 的一种方法是计算 Z 方向的间隔从零% 到 100%。 然后,每个间隔表示一个近平面和一个远平面作为 Z 轴的百分比。

图 3. 任意查看分区的 frustum

视图 frustum 任意分区

实际上,重新计算每个帧的棱锥拆分会导致阴影边缘闪闪发光。 通常接受的做法是为每个方案使用一组静态级联间隔。 在此方案中,沿 Z 轴的间隔用于描述分区 frustum 时发生的子校验和。 确定给定场景的正确大小间隔取决于多个因素。

场景几何图形的方向

对于场景几何图形,相机方向会影响级联间隔选择。 例如,一个离地面很近的相机,如足球比赛中的地面摄像头,具有与天空中的相机不同的静态级联间隔集。

图 4 显示了一些不同的相机及其各自的分区。 当场景的 Z 范围非常大时,需要更多的拆分平面。 例如,当眼睛非常接近地面平面时,但遥远的对象仍然可见,可能需要多个级联。 将 frustum 划分,以便更多的拆分靠近眼睛(其中透视锯齿正在变化最快)也是有价值的。 当大部分几何图形被合并到视图的小型部分(例如开销视图或飞行模拟器)时,需要较少的级联。

图 4. 不同的配置需要不同的 frustum 拆分

不同的配置需要不同的 frustum 拆分

(左)当几何图形在 Z 中具有高动态范围时,需要大量的级联。 (中心)当几何图形在 Z 中具有低动态范围时,多个直角几乎没有好处。 (右)当动态范围为中等时,只需要三个分区。

光和相机的方向

每个级联的投影矩阵紧贴在其相应的子图和周围。 在视图相机和光线方向是正交的配置中,级联可以紧密拟合,几乎没有重叠。 随着光线和视图相机进入平行对齐方式(图 5),重叠变大。 当光线和视图相机几乎平行时,它被称为“决斗”,并且对于大多数阴影算法来说,这是一个非常困难的场景。 限制光线和相机并不罕见,因此这种情况不会发生。 但是,CSM 的性能比本方案中的许多其他算法要好得多。

图 5. 随着光方向与相机方向的并行,级联重叠增加

级联重叠随着光线方向与相机方向 的平行而增加

许多 CSM 实现使用固定大小的 frusta。 当 frustum 以固定大小间隔拆分时,像素着色器可以使用 Z 深度为级联数组编制索引。

计算 View-Frustum 绑定

选择 frustum 间隔后,使用以下两个之一创建子frusta:适合场景并适合级联。

适合场景

可以使用相同的近平面创建所有 frusta。 这迫使级联重叠。 CascadedShadowMaps11 示例调用此技术适合场景。

适合级联

或者,可以使用实际分区间隔创建 frusta 作为近平面和远平面。 这会导致更紧密的拟合,但在决斗时退化为场景。 CascadedShadowMaps11 示例调用此技术适合级联。

图 6 中显示了这两种方法。 适合级联浪费的分辨率更少。 适合级联的问题在于,正交投影根据视锥的方向增长和收缩。 适合场景的技术通过视图 frustum 的最大大小填充正交投影,以删除在视图相机移动时出现的项目。 改进阴影深度地图的常见技术 解决了在“以纹素大小增量移动光”部分时出现的项目。

图 6. 适合场景与适合级联

适合场景,适合级联

呈现阴影映射

CascadedShadowMaps11 示例将阴影映射呈现到一个大型缓冲区中。 这是因为纹理数组上的 PCF 是 Direct3D 10.1 功能。 对于每个级联,都会创建一个视区,该视区涵盖对应于该级联的深度缓冲区部分。 空像素着色器是绑定的,因为只需要深度。 最后,为每个级联设置正确的视区和阴影矩阵,因为深度映射一次呈现到主阴影缓冲区中。

呈现场景

包含阴影的缓冲区现在绑定到像素着色器。 有两种方法可用于选择 CascadedShadowMaps11 示例中实现的级联。 这两种方法通过着色器代码进行解释。

Interval-Based 级联选择

图 7. 基于间隔的级联选择

基于间隔的级联选择

在基于间隔的选择(图 7)中,顶点着色器计算顶点的世界空间中的位置。

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

像素着色器接收内插深度。

fCurrentPixelDepth = Input.vDepth;

基于间隔的级联选择使用矢量比较和点积来确定正确的 cacade。 CASCADE_COUNT_FLAG指定级联的数目。 m_fCascadeFrustumsEyeSpaceDepths_data约束视图 frustum 分区。 比较后,fComparison 包含一个值 1,其中当前像素大于屏障,当当前级联较小时,值为 0。 点积将这些值求和到数组索引中。

        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;

选择级联后,纹理坐标必须转换为正确的级联。

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

然后,此纹理坐标用于使用 X 坐标和 Y 坐标对纹理采样。 Z 坐标用于执行最终深度比较。

Map-Based 级联选择

基于地图的选择(图 8)针对级联的四侧进行测试,以查找覆盖特定像素的最紧密地图。 顶点着色器不会计算世界空间中的位置,而是计算每个级联的视空间位置。 像素着色器循环访问级联,以便缩放和移动纹理坐标,以便为当前级联编制索引。 然后,根据纹理边界测试纹理坐标。 当纹理坐标的 X 值和 Y 值落入级联内时,它们用于采样纹理。 Z 坐标用于执行最终深度比较。

图 8. 基于地图的级联选择

基于地图的级联选择

Interval-Based 选择与 Map-Based 选择

基于间隔的选择比基于地图的选择要快一些,因为可以直接完成级联选择。 基于地图的选择必须与级联边界相交纹理坐标。

基于地图的选择在阴影映射不完全对齐时更有效地使用级联(见图 8)。

在级联之间混合

VSM(本文稍后讨论)和 PCF 等筛选技术可用于低分辨率 CSM 来生成软阴影。 遗憾的是,这会导致级联层之间的可见接缝(图 9),因为分辨率不匹配。 解决方法是在阴影映射之间创建一个带状,在该映射中对两个级联执行阴影测试。 然后,着色器根据混合带中像素的位置在两个值之间线性内插。 示例 CascadedShadowMaps11 和 VarianceShadows11 提供了可用于增加和减少此模糊带的 GUI 滑块。 着色器执行动态分支,以便绝大多数像素仅从当前级联中读取。

图 9. 级联接缝

级联接缝

(左)可以看到级联重叠的可见接缝。 (右)当级联混合在一起时,不会发生接缝。

筛选阴影映射

PCF

筛选普通阴影映射不会产生柔和模糊的阴影。 筛选硬件模糊了深度值,然后将这些模糊值与光空间纹素进行比较。 通过/失败测试产生的硬边缘仍然存在。 模糊的阴影地图只能错误地移动硬边缘。 PCF 支持对阴影映射进行筛选。 PCF 的一般思路是根据通过深度测试的子采样数计算阴影中像素的百分比。

Direct3D 10 和 Direct3D 11 硬件可以执行 PCF。 PCF 采样器的输入包括纹理坐标和比较深度值。 为简单起见,PCF 通过四点击筛选器进行解释。 纹理采样器读取纹理四次,类似于标准筛选器。 但是,返回的结果是通过深度测试的像素的百分比。 图 10 显示了如何通过四个深度测试之一的像素在阴影中为 25%。 返回的实际值是基于纹理读取的子纹素坐标的线性内插,以生成平滑渐变。 如果没有这种线性内插,四点击 PCF 只能返回五个值:{ 0.0,0.25,0.5,0.75,1.0 }。

图 10. PCF 筛选的图像,其中 25% 的选定像素被覆盖

pcf 筛选图像,其中 25% 的选定像素覆盖

还可以在没有硬件支持的情况下执行 PCF,或者将 PCF 扩展到更大的内核。 某些技术甚至包含加权内核的示例。 为此,请为 N × N 网格创建内核(例如 Gaussian)。 权重必须加起来最多 1。 然后对纹理采样 N2 次。 每个样本都按内核中的相应权重进行缩放。 CascadedShadowMaps11 示例使用此方法。

深度偏差

使用大型 PCF 内核时,深度偏差变得更加重要。 仅对像素的光空间深度与它映射到深度地图的像素进行比较才有效。 深度地图纹素的邻居引用不同的位置。 此深度可能类似,但根据场景的不同,可能大相径庭。 图 11 突出显示发生的项目。 将单个深度与阴影图中的三个相邻纹素进行比较。 其中一个深度测试错误地失败,因为它的深度与当前几何图形的计算光空间深度无关。 建议解决此问题的解决方法是使用更大的偏移量。 然而,太大的偏移量可能会导致彼得·潘宁。 计算紧近平面和远平面有助于减少使用偏移的影响。

图 11. 错误自我阴影

错误的自我阴影

错误的自我阴影结果是从将浅空间深度中的像素与阴影映射中不关联的纹素进行比较的结果。 浅空间中的深度与深度地图中的阴影纹素 2 相关。 纹素 1 大于浅空间深度,2 等于,3 小于。 纹素 2 和 3 通过深度测试,而纹素 1 失败。

使用 DDX 和 DDY 计算大型 PCF 的 Per-Texel 深度偏差

计算大型 PCF 的每个纹素深度偏差,ddxddy 是计算相邻阴影贴图纹素的正确深度偏差(假设表面为平面)。

此方法使用衍生信息将比较深度与平面相吻合。 由于此方法在计算上很复杂,因此仅当 GPU 具有备用计算周期时,才应使用它。 使用非常大的内核时,这可能是唯一一种在不导致 Peter Panning 的情况下删除自影项目的方法。

图 12 突出显示了问题。 光空间的深度以正在比较的纹素而闻名。 与深度图中的相邻纹素相对应的光空间深度未知。

图 12. 场景和深度地图

场景和深度地图

呈现的场景显示在左侧,带有示例纹素块的深度地图显示在右侧。 眼空纹素映射到块中心标有 D 的像素。 此比较准确。 与邻居 D 未知的像素相关的眼部空间中的正确深度。 仅当假定像素与 D 相同的三角形相关时,才能将相邻纹素映射回眼睛空间。

深度以与光空间位置关联的纹素而闻名。 深度图中相邻纹素的深度未知。

在高级别上,此方法使用 ddxddy HLSL作来查找光空间位置的衍生值。 这是非常数的,因为派生运算返回光空间深度相对于屏幕空间的渐变。 若要将其转换为光空间深度相对于光空间的渐变,必须计算转换矩阵。

着色器代码的说明

算法的其余部分的详细信息作为执行此作的着色器代码的说明提供。 可以在 CascadedShadowMaps11 示例中找到此代码。 图 13 显示了光空间纹理坐标如何映射到深度地图,以及如何使用 X 和 Y 中的派生体来创建转换矩阵。

图 13. 屏幕空间到浅空间矩阵

屏幕空间到光空间矩阵

X 和 Y 中光空间位置的衍生值用于创建此矩阵。

第一步是计算光视空间位置的衍生值。

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

Direct3D 11 类 GPU 通过并行运行 2 个× 2 个像素的四分之二,并减去 X 中邻居的纹理坐标(ddx)和 Y 中的邻居(ddy)来计算这些派生。 这两个派生体构成 2 × 2 矩阵的行。 在此矩阵的当前形式中,此矩阵可用于将屏幕空间相邻像素转换为光空间斜率。 但是,需要此矩阵的反函数。 需要将光空间相邻像素转换为屏幕空间斜率的矩阵。

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

图 14. 浅色到屏幕空间

光空间到屏幕空间

然后,此矩阵用于转换当前纹素上方和右侧的两个纹素。 这些邻居表示为当前纹素的偏移量。

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

矩阵创建的比率最终乘以深度派生数,以计算相邻像素的深度偏移量。

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

这些权重现在可以在 PCF 循环中使用,以向位置添加偏移量。

    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 和 CSM

PCF 不适用于 Direct3D 10 中的纹理数组。 若要使用 PCF,所有级联都存储在一个大型纹理图集中。

Derivative-Based 偏移量

为 CSM 添加基于派生的偏移量会带来一些挑战。 这是因为分流控制中的派生计算。 之所以出现问题,是因为 GPU 运行的基本方式。 Direct3D11 GPU 在 2 个像素的 2 × 2 个象限上运行。 若要执行派生,GPU 通常从相邻像素的同一变量副本中减去当前像素的变量副本。 这种情况的方式因 GPU 而异。 纹理坐标由基于地图或基于间隔的级联选择确定。 像素象限中的某些像素选择与其余像素不同的级联。 这会导致阴影映射之间的可见接缝,因为基于派生的偏移量现在完全错误。 解决方法是在光视图空间纹理坐标上执行派生。 对于每个级联,这些坐标都是相同的。

PCF 内核的填充

如果未填充阴影缓冲区,则 PCF 内核索引位于级联分区之外。 解决方案是将级联的外部边缘填充一半的 PCF 内核大小。 这必须在选择级联的着色器中实现,并且必须在投影矩阵中呈现足够大的级联,以便保留边框。

方差阴影映射

VSM(请参阅 Donnelly 和 Lauritzen 方差阴影映射)启用直接阴影映射筛选。 使用 VSM 时,可以使用纹理筛选硬件的所有功能。 可以使用三线性和异性(图 15)筛选。 此外,VSM 可以直接通过卷积模糊。 VSM 确实有一些缺点;必须存储两个深度数据的通道(深度和深度平方)。 当阴影重叠时,光出血很常见。 但是,它们的工作方式非常出色,分辨率较低,可与 CSM 结合使用。

图 15. 异向异性筛选

异性筛选

算法详细信息

VSM 通过呈现深度和平方到双声道阴影地图的深度来工作。 然后,可以像普通纹理一样模糊和筛选此双声道阴影图。 然后,该算法在像素着色器中使用 Chebychev 的不等性来估计通过深度测试的像素区域的分数。

像素着色器提取深度和深度平方值。

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

执行深度比较。

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

如果深度比较失败,则估计点亮的像素百分比。 方差计算为平方的平均值减去平均平方。

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

fPercentLit 值估计为 Chebychev 的不等性。

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

浅出血

VSM 的最大缺点是轻微出血(图 16)。 当多个阴影投射器沿边缘相互遮挡时,将发生光出血。 VSM 根据深度差异对阴影的边缘进行着色。 当阴影相互重叠时,应隐藏的区域中心存在深度差异。 这是使用 VSM 算法时出现的问题。

图 16. VSM 光出血

与光出血

问题的一个部分解决方法是将 fPercentLit 提升到电源。 这会影响抑制模糊效果,这可能导致深度差异很小的项目。 有时存在一个神奇的价值,可以缓解问题。

fPercentLit = pow( p_max, MAGIC_NUMBER );

提高功率百分比的替代方法是避免阴影重叠的配置。 即使是高度优化的阴影配置也对光、相机和几何图形具有多种约束。 使用更高分辨率的纹理也减少了光出血。

分层方差阴影映射(LVSM)解决了问题,代价是将 frustum 分解成垂直于光的层。 在使用 CSM 时,所需的映射数将相当大。

此外,VSM 论文的共同作者安德鲁·劳里森(Andrew Lauritzen)和LVSM论文的作者讨论了如何将指数阴影映射(ESM)与 VSM 相结合,以对抗 Beyond3D 论坛的光混合。

具有 CSM 的 VSM

示例 VarianceShadow11 结合了 VSM 和 CSM。 组合相当简单。 此示例遵循与 CascadedShadowMaps11 示例相同的步骤。 由于未使用 PCF,因此阴影在双通道可分离卷积中模糊。 不使用 PCF 也使示例能够使用纹理数组而不是纹理图集。 纹理阵列上的 PCF 是 Direct3D 10.1 功能。

使用 CSM 的渐变

将渐变与 CSM 配合使用可以生成两个级联之间的接缝,如图 17 所示。 示例指令使用像素之间的衍生值来计算筛选器所需的信息,例如 mipmap 级别。 这尤其会导致 mipmap 选择或异性筛选出现问题。 当象限中的像素采用着色器中的不同分支时,GPU 硬件计算的派生体无效。 这会导致阴影图上出现锯齿状接缝。

图 17. 由于具有不同流量控制的异性筛选,级联边界上的接缝

由于具有分化流控制级联边框上的接缝

通过计算光视空间中位置的衍生值来解决此问题:浅视图空间坐标不特定于所选级联。 计算派生可以按投影纹理矩阵的缩放部分缩放到正确的 mipmap 级别。

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

与 PCF 的标准阴影相比的 VSM

VSM 和 PCF 都尝试近似通过深度测试的像素面积的分数。 VSM 适用于筛选硬件,并且可以使用可分离的内核模糊化。 可分离卷积内核比完整内核要便宜得多。 此外,VSM 将一个光空间深度与光空间深度地图中的一个值进行比较。 这意味着 VSM 与 PCF 没有相同的偏移问题。 从技术上看,VSM 在更大的区域进行采样深度,以及执行统计分析。 这比 PCF 更精确。 在实践中,VSM 在混合方面做得非常好,这会导致需要较少的偏移量。 如上所述,VSM 的头号缺点是轻微出血。

VSM 和 PCF 表示 GPU 计算能力和 GPU 纹理带宽之间的权衡。 VSM 需要执行更多数学运算才能计算方差。 PCF 需要更多的纹理内存带宽。 大型 PCF 内核可能会因纹理带宽而快速成为瓶颈。 随着 GPU 计算能力比 GPU 带宽更快增长,VSM 正在成为这两种算法的更实用。 由于混合和筛选,VSM 在分辨率较低的阴影映射中看起来也更好。

总结

CSM 为透视别名问题提供了解决方案。 有几个可能的配置可以获取游戏所需的视觉保真度。 PCF 和 VSM 广泛使用,应与 CSM 结合使用以减少别名。

引用

唐纳利、W.和劳里森,A.方差阴影地图。 在 SI3D '06:2006 年交互式 3D 图形和游戏研讨会的诉讼。 2006. pp. 161–165. 纽约,纽约,美国:ACM出版社。

劳里森、安德鲁和麦考尔,迈克尔。 分层方差阴影映射。 图形界面 2008 年 5 月 28 日至 30 日,加拿大安大略温莎。

恩格尔, 沃夫刚 F. 第 4 节。 级联阴影映射。 ShaderX5、高级渲染技术、Wolfgang F. Engel、Ed。 查尔斯河媒体,波士顿,马萨诸塞州。 2006. pp. 197–206.