【URP】Unity[RendererFeatures]屏幕空间阴影ScreenSpaceShadows

【从UnityURP开始探索游戏渲染】专栏-直达

Screen Space Shadows(屏幕空间阴影)是Unity URP中通过屏幕空间数据实时计算阴影的技术,其核心原理是通过深度/法线信息重建世界坐标后与阴影贴图比较生成阴影。以下是详细分析:

技术原理与发展历史

  • 基础原理‌:传统Shadow Mapping需从光源视角生成深度图,再与摄像机视角深度比较。而Screen Space Shadows直接在屏幕空间计算,利用_CameraDepthTexture和_MainLightShadowmapTexture进行深度比较。
  • 技术演进 ‌:
    • 早期采用标准Shadow Mapping存在精度问题
    • 引入级联阴影(CSM)解决大场景问题
    • URP 14+版本通过ScreenSpaceShadowResolvePass实现优化
  • 性能对比‌:相比传统方案,屏幕空间阴影减少Overdraw但增加Depth Prepass开销。

原理

核心原理

  • 双阶段渲染机制

    • 深度图生成阶段 ‌:从光源视角渲染场景,将最近表面深度值存储到_MainLightShadowmapTexture
    • 阴影判定阶段‌:在摄像机视角渲染时,将像素坐标转换到光源空间,比较当前深度与ShadowMap中的深度值,若当前深度更大则判定为阴影区域
  • 级联阴影优化CSM

    通过将视锥体划分为多个层级(如4级),近处使用高分辨率ShadowMap,远处使用低分辨率,平衡性能与精度

关键实现步骤

  • ShadowCaster Pass

    c 复制代码
    hlsl
    Pass {
        Tags {"LightMode"="ShadowCaster"}
        HLSLPROGRAM
        #pragma vertex ShadowPassVertex
        #pragma fragment ShadowPassFragment
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
        // 应用深度偏移防止自阴影伪影
        float3 _ShadowBias; // x:DepthBias, y:NormalBias
        ENDHLSL
    }

    该Pass专用于生成深度图,通过_ShadowBias参数控制深度偏移

  • 光源空间矩阵计算

    构建_MainLightWorldToShadow矩阵,将世界坐标转换到光源裁剪空间:

    csharp 复制代码
    csharp
    Matrix4x4 worldToView = light.transform.worldToLocalMatrix;
    Matrix4x4 viewToProj = GL.GetGPUProjectionMatrix(light.projectionMatrix, false);
    _MainLightWorldToShadow = viewToProj * worldToView;

    该矩阵用于在着色器中计算阴影坐标

  • 阴影采样与混合

    在片元着色器中通过PCF(Percentage Closer Filtering)实现软阴影:

    c 复制代码
    hlsl
    float SampleShadowmap(float4 shadowCoord) {
        float shadow = 0;
        for(int i = 0; i < 4; i++) {
            shadow += SAMPLE_TEXTURE2D_SHADOW(
                _MainLightShadowmapTexture,
                sampler_MainLightShadowmapTexture,
                shadowCoord.xyz + poissonDisk[i] * _ShadowSoftness
            );
        }
        return shadow / 4;
    }

    使用泊松圆盘采样实现边缘柔化

完整URP示例

  • ShadowFeature.cs

    csharp 复制代码
    using UnityEngine.Rendering.Universal;
    
    public class ShadowFeature : ScriptableRendererFeature {
        class ShadowPass : ScriptableRenderPass {
            public override void Execute(ScriptableRenderContext context, ref RenderingData data) {
                // 1. 配置光源参数
                var shadowLight = data.lightData.mainLightIndex;
                if (shadowLight == -1) return;
    
                // 2. 渲染ShadowMap
                CommandBuffer cmd = CommandBufferPool.Get("ShadowMap");
                context.ExecuteCommandBuffer(cmd);
                CommandBufferPool.Release(cmd);
            }
        }
    }
  • ShadowShader.shader

    c 复制代码
    Shader "Custom/ShadowReceiver" {
        Properties {
            _ShadowStrength("Shadow Strength", Range(0,1)) = 0.5
        }
        SubShader {
            Pass {
                HLSLPROGRAM
                #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
                float _ShadowStrength;
    
                float4 frag(v2f i) : SV_Target {
                    float shadow = MainLightRealtimeShadow(i.shadowCoord);
                    return lerp(1, shadow, _ShadowStrength);
                }
                ENDHLSL
            }
        }
    }

参数说明

参数 作用 典型值 技术原理
Shadow Distance 阴影最大渲染距离 50-100 超出距离后淡出阴影
Cascade Count 级联分层数 2/4 动态分配纹理分辨率
Depth Bias 深度偏移量 0.01-0.1 防止自阴影伪影
Normal Bias 法线偏移量 0.5-2.0 改善尖锐边缘阴影

该实现结合了URP的ShadowCaster管线与自定义Shader,通过级联阴影和PCF滤波达到平衡性能与质量的效果

参数说明与用例

参数 说明 典型值
Strength 阴影浓度 0.5-1.0
Bias 防止自阴影 0.05-0.2
Normal Bias 法线偏移 0.3-0.7
Resolution 精度等级 High/Very High

实际应用场景‌:

  • 动态物体阴影‌:配合CSM实现高质量移动物体阴影
  • 性能敏感场景‌:中低端设备可降低Resolution至Medium
  • 特效增强‌:结合Decal系统实现弹孔等动态贴花效果

完整配置流程

  • 创建Renderer Feature并附加到URP Asset
  • 编写Shader处理深度比较逻辑
  • 通过Frame Debugger验证阴影生成阶段
  • 调整Bias参数消除阴影瑕疵

关键Shader需包含以下处理:

  • 深度重建世界坐标
  • 光源空间坐标转换
  • 阴影强度插值计算

【从UnityURP开始探索游戏渲染】专栏-直达

(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)