【从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
chlsl 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矩阵,将世界坐标转换到光源裁剪空间:csharpcsharp Matrix4x4 worldToView = light.transform.worldToLocalMatrix; Matrix4x4 viewToProj = GL.GetGPUProjectionMatrix(light.projectionMatrix, false); _MainLightWorldToShadow = viewToProj * worldToView;该矩阵用于在着色器中计算阴影坐标
-
阴影采样与混合
在片元着色器中通过PCF(Percentage Closer Filtering)实现软阴影:
chlsl 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
csharpusing 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
cShader "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开始探索游戏渲染】专栏-直达
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)