实时阴影技术演进与 URP 自定义阴影管线
行文逻辑:宏观总览 → 底层基石 → 算法进阶 → 管线工程落地 → 源码解析。
本文同时给出在 Unity 2022 LTS URP 下手写一套支持 彩色阴影、半透明阴影、PCSS 软阴影、SRP Batcher 兼容 的复合阴影管线的工程方案。
一、引言:光与影的极限博弈
阴影是构建场景空间感与画面真实感的决定性因素。一个没有正确接地阴影的物体永远像悬在空中的纸片,PBR 再炫的金属高光也救不回来。
但实时渲染中的阴影,本质是画质需求与**硬件预算(ALU、显存带宽、内存)**之间的极限平衡:
- 想要柔和的边缘 → 多次 PCF 采样,ALU 暴涨;
- 想要大世界覆盖 → 级联 / 虚拟阴影贴图,显存与带宽吃紧;
- 想要物理正确 → 硬件光追,性能直接砍半。
本文将从底层 Shadow Map 出发,沿着「软阴影 → 屏幕空间 → 工程落地」的路径走完一遍,最后落在我自己用 RenderFeature 手写的 PCSS 复合管线上。
二、现代阴影技术流派全景图
五大流派一图流
横向对比表
| 流派 | 代表技术 | 核心优势 | 核心劣势 | 典型应用 |
|---|---|---|---|---|
| 几何与深度 | CSM | 工业标准,全硬件兼容 | 边缘走样、软化开销大 | 大世界主光源 |
| UE5 VSM | 极高分辨率,适配 Nanite | 页表管理复杂 | 次世代超高多边形 | |
| 统计滤波 | VSM / EVSM | 漏光(Light Bleeding) | 风格化 / 离线烘焙 | |
| 屏幕空间 | Contact Shadows | 极低开销解决悬浮感 | 仅几厘米范围 | CSM 补漏工序 |
| SSS | 高精度微小几何细节 | 屏幕外信息丢失 | 头发、草丛、铁丝网 | |
| 物理光追 | SDF Shadows | 大尺度高效软阴影 | 不支持蒙皮动画 | 远景建筑、地形 |
| RT Shadows | 物理正确,无漏光 | 性能与硬件门槛高 | 3A 电影级画质 | |
| 预计算 | Shadowmask | 静态高质 + 动态融合 | 烘焙耗时、显存占用 | 中大型项目标配 |
五大流派详解
在展开技术细节之前,先用 5 段话回答一个很多新人会有的疑问:面对同一束光、同一个遮挡物,为什么要发明这么多不同算法? 答案是——每一派都在解决前一派暴露出来的问题。
① 几何与深度测试派:所有流派的起点
这是阴影技术的”祖师爷”,核心逻辑只有一句话:「从光源视角渲染一遍场景,把每个像素离光源最近的距离记下来;主相机渲染时,把当前着色点变换回光源空间,深度更大的就是被挡住了」。
代表算法是 1978 年 Lance Williams 提出的 Shadow Map,直到今天它依然是几乎所有实时渲染管线的基石。但它有两个原生缺陷:
- 走样(Aliasing):ShadowMap 分辨率有限,一个纹素覆盖一片世界空间,边缘必然出现锯齿;
- 精度(Precision):远处一张 2K² 的贴图覆盖几公里地形,平均每米只有几个纹素,根本不够用。
为了解决精度问题,CSM (Cascaded Shadow Maps) 把视锥按深度切成 2~8 段,近段高精度、远段低精度,是当今所有大世界游戏的标配。UE5 VSM (Virtual Shadow Maps) 则把虚拟纹理思想搬过来——逻辑上让 ShadowMap 拥有 16K² 分辨率,但只为屏幕上实际可见的页(Page)分配显存与计算资源,是为 Nanite 那种亿级面几何量身定制的。Cube Map 点光源阴影 则把这套机制套用到 6 个面的立方体上,每盏点光源等于渲染 6 次场景,所以引擎一般会严格限制运行时投影点光源的数量。
② 统计与滤波派:用概率论换软阴影
第一派的硬阴影是”非黑即白”的——深度比较只能产生 0 或 1。要做软阴影,最直观的办法是 PCF(Percentage Closer Filtering) 多次采样取平均,但这是
VSM (Variance Shadow Maps) 的革命性想法:与其问”这一点有没有被挡”,不如问”被挡的概率上界是多少”。把深度分布抽象成一个随机变量
代价是 漏光(Light Bleeding)——当邻域内同时存在前景和背景遮挡物时,方差被夸大、概率上界变宽松,本应深阴影的区域漏出光来。EVSM 把深度先做
③ 屏幕空间派:补漏与细节增强专家
前两派都基于”光源视角的深度图”。屏幕空间派则完全不需要光源视角,只用主相机已经渲染好的 _CameraDepthTexture 做事。
具体做法:从着色点出发,沿光源方向在屏幕空间步进(Raymarching),每一步把世界坐标投影回屏幕、采样深度图,判断当前光线是否已经被前景物体挡住。
- Contact Shadows:射线极短(几厘米)、采样数低(4-8 次),专门修补 CSM 在物体接触地面时的漏光(Peter Panning),让物体”扎根”在场景里;
- Screen Space Shadows (SSS):射线较长(几米)、采样数高(16-64 次),捕捉头发、草、铁丝网这类几何细节小到 ShadowMap 根本采不到的东西,是大世界游戏中近距特写画质的关键加成。
它们的共同短板是依赖屏幕信息——物体一旦移出视野范围,阴影就会突兀消失(Off-screen Artifact),所以永远只能作为「主阴影方案的补充」,而不是替代。
④ 物理与光追派:终极解,但贵
前三派本质上都是 基于光栅化的近似——用各种 hack 在屏幕或贴图上模拟”这条光线有没有被挡”。物理派直接把这件事做实:发射真实光线测试相交。
- SDF (Mesh Distance Field) Shadows:离线把场景几何编码成 3D 距离场纹理,运行时用 Sphere Tracing 计算软阴影。无 ShadowMap 走样、远距离软阴影质量极佳,性能比硬件光追便宜得多。短板是形变动画支持差——蒙皮骨骼的每个 Pose 都重新生成 SDF 不现实,所以仅适用于静态场景。UE5 Lumen 的远场阴影就是基于这套机制;
- Hardware RT Shadows:调 GPU 的 RT Core 发射光线,根据面光源大小天然得到物理正确的软阴影——光源越大、距离越远,阴影越软;完全无漏光、无走样。代价是性能开销巨大、需要 RTX 30+ / RX 6000+ / Apple M3+ 等支持光追的硬件,且 BVH 维护本身也是一笔不菲的运行时开销。这是 3A 厂商「能开就开,开不起就 fallback CSM」的方案。
⑤ 预计算混合派:拿空间换时间
终极的「移动端 / 主机优化」流派。核心思路是把光照与阴影计算搬到离线,运行时只做查表。
- Lightmaps:直接把光照 + 阴影烘焙到纹理上,运行时零计算开销,可在最低端硬件上跑出主机级画质。但缺点也很硬——完全不支持动态物体和动态光源,所有可移动的角色 / 道具都得用 Light Probe 单独打光;
- Shadowmask:是 Lightmap 的进化版——贴图里只存「阴影遮挡系数」(一个 0~1 的值,不是最终颜色)。当动态角色走到烘焙好的阴影区域时,引擎会拿这个系数与实时阴影做加权融合,避免动态阴影 + 静态阴影叠加导致的”双重阴影”穿帮。这是当前中大型项目兼顾性能与表现的事实标准(Unity Mixed Lighting 的 Shadowmask 模式即此)。
工业界主流复合策略
CSM 主力 + SSS 补细节 + Contact Shadows 接地缝合
这一组合至今仍是绝大多数项目的最优解,本文第六、七章手写实现的彩色 ShadowMap 管线本质上就是这条路线的「定制化扩展」。
三、阴影映射的底层基石与痛点规避
双 Pass 机制
ShadowMap 的核心思想:把光源当成相机渲染一遍场景,记录每条光线最先击中的那个点的深度。 主相机渲染时再把着色点变换回光源空间,与该深度比较。
sequenceDiagram
participant L as 光源相机
participant SM as ShadowMap (Depth RT)
participant MC as 主相机
participant FS as Fragment Shader
L->>SM: Pass 1 - 光源视角光栅化, 写入深度 d_q
Note over SM: 仅 ColorMask 0, 只写 ZBuffer
MC->>FS: Pass 2 - 主视角光栅化
FS->>FS: 顶点 worldPos × 光源 V × P → shadowCoord
FS->>SM: 用 shadowCoord.xy 采样 d_q
FS->>FS: 比较 currentDepth (d_p) vs d_q
alt d_p > d_q + bias
FS->>FS: 在阴影中, 衰减光照
else
FS->>FS: 完全照亮
end
经典视觉瑕疵
1. 阴影痤疮 Shadow Acne
ShadowMap 分辨率有限,一个纹素覆盖斜面上的一片区域,该区域内的所有片段都拿同一个深度做比较。当光线斜着射入表面时,部分片段的深度会略大于纹素记录的深度,被错误地判定为「自遮挡」,呈现条纹噪点。
2. 彼得潘现象 Peter Panning
为修复痤疮强行拉大 bias,会让阴影远离投射者,物体看起来「悬浮」在地面上、和影子断了连接。

📷 配图建议:三连对比 GIF——左:无 bias 的条纹痤疮;中:bias 过大的 Peter Panning 悬浮感;右:自适应 bias 修复后的干净接地效果。
资源未上传时此处占位。
自适应 Shadow Bias
Bias 应当随表面入射角和 ShadowMap 像素覆盖范围动态变化:
1 | |
要点:
A把视锥体大小折算到一个 ShadowMap 纹素覆盖的世界距离;B在掠射角下放大 bias。filterRadiusUV给 PCF 留接口——滤波半径越大,邻域内可能落入的纹素越多,需要的 bias 越大。
bias 还有 法线偏移(Normal Bias) 的变体——直接把世界坐标沿法线推一小段后再投影,对斜面友好;我的 ColoredShadowCaster Pass 就是这么做的:
1 | |
四、迈向真实:软阴影的数学之美
4.1 PCF:从硬阴影到模糊边缘
PCF(Percentage Closer Filtering) 不是对深度图做模糊(这是错的,下面 VSM 章节会解释为什么),而是把”深度比较”这个二值结果做平均:
1 | |
采样范围越大、采样数越多,阴影越软,但 ALU 也越贵。我在 SampleSoftShadowPCF 中按 sampleCount 分了 2×2 与 3×3 两档,避免低端机跑满 9 次采样。
4.2 Poisson 盘采样:抗规则噪点
3×3 网格采样会产生明显的方格走样。换成 Poisson 盘 的预定义伪随机采样点,能在相同采样数下显著提高视觉质量:
1 | |
每像素旋转是关键:相邻像素若使用同一组采样点,会暴露出 Poisson 盘本身的”花纹”。
GetRotationMatrix(rotation)接收一个伪随机角度,按像素打散这种 pattern。
4.3 PCSS:物理近似的”近实远虚”
真实阴影满足:遮挡物离接收面越远,半影越大。PCSS(Percentage Closer Soft Shadows,Fernando 2005)用相似三角形把这件事用 ShadowMap 模拟出来。
flowchart LR
A[Shading Point] --> B[Step 1: Blocker Search
FindBlockerDistance]
B -->|无遮挡| Z[直接返回 1.0]
B -->|平均遮挡深度 d_blocker| C[Step 2: Penumbra Estimation
相似三角形]
C --> D[Step 3: PCF / Poisson Filtering
动态半径]
D --> E[最终 visibility]
Step 1 · 遮挡物搜索
在以 shading point 为中心的一个 UV 圆盘内随机采样深度图,统计比 receiver 浅的点的平均深度。搜索半径与光源大小、接收点深度成正比:
1 | |
边界处理用 uvToBorder 限幅,防止搜索域跑到 ShadowMap 外面采到 (0, 0) 黑边。
Step 2 · 半影尺寸估计
1 | |
NVIDIA 白皮书的实现额外乘
NEAR_PLANE / coords.z,把 light size 投影到 ShadowMap 平面。这一步对正交投影的方向光意义不大(深度均匀),所以我在自己的实现里省略了,结果同样可控。
Step 3 · 自适应 PCF 滤波
把第二步算出的 penumbraSize 喂回 PCF 或 Poisson 采样作为滤波半径——远处遮挡物自动得到更大的模糊核,”近实远虚” 自然涌现。

📷 配图建议:一根长投影物(电线杆 / 旗杆 / 树干)的特写——靠近根部阴影边缘锐利,远端逐渐柔化弥散。最能直观展示 PCSS 相对于固定半径 PCF 的优势。
4.4 VSM 与切比雪夫不等式
为什么 PCF 慢,VSM 快?
PCF 必须先采样、再比较、再平均,比较是非线性操作(step),所以不能预先对深度图做高斯模糊 —— 模糊后的深度无法表达”该纹素附近遮挡的概率分布”。
VSM 的天才之处:改变阴影计算的物理意义。它不再问”我被遮挡了吗?”,而是问”我被遮挡的概率上界是多少?“
数学基础:单边切比雪夫不等式
只要知道期望
为什么要存 ?
- 在 ShadowMap 上做 Box / Gaussian 滤波 → 得到该纹素邻域内深度的期望
; - 同时存
通道、做相同滤波 → 得到 ; - 由方差公式
,得到方差。
VSM 完整流程
flowchart TD
A[Pass 1: 光源视角渲染
RT 输出 d 与 d²] --> B[对该 RT 做 Box / Gaussian 滤波]
B --> C[Pass 2: 主相机视角着色]
C --> D{currentDepth t 与 E_d 比较}
D -->|t ≤ E_d| E[完全照亮 visibility = 1]
D -->|t > E_d| F[切比雪夫估算 P_max]
F --> G[visibility = 1 - P_max]
漏光(Light Bleeding)
VSM 的命门:当一个区域内同时存在前景遮挡物和远处背景时,方差
工业上用 EVSM(Exponential VSM) 把深度先
五、屏幕空间技术:微观细节的极致压榨
5.1 共同的底层:屏幕空间向光步进
Contact Shadows 与 Screen Space Shadows 底层算法几乎一样——从着色点出发,沿光源方向在屏幕深度缓冲里做 Raymarching,每一步采样 _CameraDepthTexture 检查是否被前景遮挡。
1 | |
5.2 同算法、不同参数 → 不同用途
| 维度 | Contact Shadows | Screen Space Shadows |
|---|---|---|
| 设计目的 | 修补 Peter Panning,让物体”扎根” | 高精度近距全局阴影、补 SM 精度 |
| 射线长度 | 极短(几厘米) | 长(几米甚至更远) |
| 步进次数 | 4–8 | 16–64 |
| 性能开销 | 极低,cache 命中率高 | 高,跨像素带宽压力大 |
| 屏幕外伪影 | 几乎无(距离太短) | 明显(边缘物体阴影闪烁) |
5.3 URP 官方 Screen Space Shadows 解读
URP 的 Hidden/Universal Render Pipeline/ScreenSpaceShadows 走的是全屏 Pass:用相机深度反推世界坐标,调 MainLightRealtimeShadow(coords),把 CSM 的采样结果烘到屏幕空间一张 RT 上,后续 ForwardLit 直接读这张 RT,避免每个 fragment 重复采样级联:
1 | |
这是「屏幕空间阴影」一词的另一层含义——阴影计算结果的屏幕空间缓存,不是 raymarching。
5.4 二次元风格化:透视空间钳制阈值
二次元角色渲染常用非标准化的轻量 SSS,思路是「用一个视图空间偏移采样深度图差值」,配合透视因子在 NDC 空间钳制阈值:
1 | |
只采一次(不步进)、用透视因子让远近偏移视觉一致——表现力够用,性能比真 raymarching 便宜一个数量级。
六、URP 管线深度定制:彩色与半透明阴影工程落地
6.1 为什么要自己写一套?
Unity 默认 ShadowCaster Pass ColorMask 0,只写深度,不写颜色。这在以下场景下是硬伤:
- 彩色玻璃 / 教堂窗户:阴影应该带颜色;
- 半透明 Alpha 阴影:默认要么不投,要么 Dither 抖动;
- 风格化二次元:希望阴影色调可调(暖色阴影、青色阴影)。
解决方案:手写一个 RenderFeature,输出彩色 RT + 深度 RT,在主 Shader 里采样。
6.2 整体架构
flowchart TB
subgraph CPU [C# - ScriptableRendererFeature]
A[Create
Init RTHandles, 注入 BeforeRenderingShadows]
B[AddRenderPasses
过滤主相机, 主光源 = Directional]
C[Setup
计算 LightView × Projection
SetGlobalMatrix _ColoredShadowMatrix]
end
subgraph GPU [GPU - ColoredShadowCaster Pass]
D[Configure
双 RT: ARGB32 + RFloat
ConfigureTarget MRT]
E[Execute
SetView/Projection
DrawRenderers Opaque + Transparent]
F[Fragment
SV_Target0: 颜色 × _ShadowColor
SV_Target1: linearDepth]
end
subgraph FWD [GPU - ForwardLit Pass]
G[采样 _ColoredShadowMap
+ _DepthShadowMap]
H[SampleCustomShadowMap
支持 Hard / PCF / Poisson / PCSS]
I[CalculatePBRLighting
shadowColor 参与能量守恒]
end
A --> B --> C --> D --> E --> F --> G --> H --> I
6.3 矩阵推导:手写 LightView × Projection
不同光源类型的光源空间投影方式天然不同——本文聚焦平行光,但要让架构具备扩展性,三类光源的处理思路都得理清楚:
1 | |
三类光源处理方式对比:
| 光源类型 | 投影矩阵 | Pass 次数 | 阴影贴图形态 |
|---|---|---|---|
| Directional | 正交 (Matrix4x4.Ortho) |
1(CSM 时按级联次数 N) | 2D RT |
| Spot | 透视 (Matrix4x4.Perspective),FOV = spotAngle |
1 | 2D RT |
| Point | 透视,FOV = 90°,6 面 | 6 | Cube RT (TextureCube) |
点光源的工程取舍:6 次 Pass 在低端设备上是灾难,所以另一条路是 Dual-Paraboloid Mapping (DPSM)——把球面贴图压缩到 2 张抛物面贴图上(前 / 后半球各一张),只需 2 次 Pass。代价是边缘扭曲、采样不均,因此它适合非主光源、近距离的辅助点光源(如室内灯泡、火把),主光源仍然走 Cube Map。
本文的 RenderFeature 默认仅处理平行光(
m_MainLight.lightType != LightType.Directional时直接return跳过整个 Pass),聚光灯与点光源是后续扩展点——只需把GetProjectionMatrix的分支逻辑接通、把 Configure 阶段的 RT 类型按需换成TextureCube,复用整套 MRT + 双 RT 框架即可。
1 | |
z 轴翻转放在 C# 端而非 Shader 内:Shader 拿到的就是干净的
mul(_ColoredShadowMatrix, worldPos),可读性、可调试性都提升一档;同时省了每帧每像素一次 z 翻转的 ALU。
6.4 多渲染目标 MRT:一次 Pass 写两张 RT
1 | |
Fragment 端配套两个 SV_Target:
1 | |
6.5 注入时机:BeforeRenderingShadows
1 | |
赶在 URP 内部 Shadow / Opaque / Transparent 队列之前生成,确保后续任何材质都能通过 _ColoredShadowMap / _DepthShadowMap 全局纹理读取到。
6.6 Profiling 接入:让 Frame Debugger 层级分明
1 | |
ProfilingScope + CommandBufferPool 是 URP 自定义 Pass 的标准接入范式:
- Frame Debugger 中显示为独立分组节点;
- Unity Profiler 自动记录 GPU 毫秒级耗时;
CommandBufferPool复用 CommandBuffer,避免每帧 GC。
6.7 性能兜底:Dither 抖动半透明阴影
不是所有平台都吃得下 ARGB32 + RFloat 双 RT。移动端可用 Dither + AlphaClip 退化方案:根据屏幕坐标 + alpha 查 _DitherMaskLOD 3D 抖动表,决定该像素是否丢弃,让深度图本身呈现”半透明”分布。
1 | |
Blue Noise 比 Bayer 在低频区域分布更均匀,配合双采样平均,能在 4×4 抖动核下做出接近真半透明的视觉效果,几乎零 ALU 开销。
6.8 Unity 6 与 Render Graph:迁移路径前瞻
本文的 ColoredShadowMapPass 走的是 传统 ScriptableRendererFeature 路线(Unity 2022 LTS / URP 14):手动 cmd.GetTemporaryRT 申请 RT、ConfigureTarget 绑定、FrameCleanup 释放,资源生命周期完全由开发者把控。这条路在 Unity 6(URP 17+)已经进入维护模式——Render Graph API 才是新的官方推荐。
范式转变:过程式 → 声明式
Render Graph 的核心变化是把”过程式的命令记录”改成声明式的图编排:你不再”立刻申请一张 RT 并往里写”,而是”声明这个 Pass 需要这些资源、产出哪些资源”,框架自己分析整张图的依赖来调度:
1 | |
Render Graph 带来的三个核心优化
- 资源生命周期自动管理:Render Graph 分析整张图后,只在真正需要的时间窗口分配显存。阴影 RT 只在 Shadow Pass → ForwardLit Pass 之间存活,自动释放,无需手动
ReleaseTemporaryRT,也杜绝了忘释放导致的显存泄漏; - Memoryless RT 与 Tile Memory 优化:在 Apple Silicon / Adreno / Mali 等 TBDR (Tile-Based Deferred Rendering) GPU 上,Render Graph 能识别”该 RT 仅在同一 Pass 内被读写”,直接放在片上 Tile Memory,完全不写回 DRAM——这对移动端阴影管线带宽优化是降维打击;
- 资源别名(Aliasing):多个生命周期不重叠的 RT 可复用同一块物理显存,显存峰值大幅下降,对开 4K + HDR + 多 Pass 的项目是刚需。
迁移要点总结
把当前 RenderFeature 代码搬到 Render Graph 的对照表:
| Legacy API(本文方案) | Render Graph 对应 |
|---|---|
cmd.GetTemporaryRT |
renderGraph.CreateTexture(TextureDesc) |
ConfigureTarget (MRT) |
builder.UseTextureFragment(rt, slotIdx) |
Execute() 内 CommandBuffer |
builder.SetRenderFunc((data, ctx) => …) |
FrameCleanup |
不需要(自动) |
Shader.SetGlobalMatrix |
builder.SetGlobalTextureAfterPass 或常规 SetGlobal |
Compatibility Mode 的去留:Unity 6.0 仍提供 Render Graph Compatibility Mode 让旧 RenderFeature API 跑起来,但 6.1 起默认关闭,6.2 起预计完全移除。如果项目现在还在 URP 14(Unity 2022 LTS),本文方案可直接落地;若已切到 URP 17+(Unity 6),建议一步到位用 Render Graph 重写——语义不变,但显存与带宽效率显著提升,对移动端项目尤为关键。

📷 配图建议:本文方案的最终成果展示——一束阳光透过彩色玻璃窗(红 / 蓝 / 黄三色),在地板上投射出对应色调的阴影光斑,配合 PCSS 远处柔化效果。这是「彩色阴影 + 半透明 Alpha + PCSS 软阴影」三大特性同框的视觉高潮。
七、PCSS 复合软阴影 Shader 架构
7.1 Uber Shader 宏定义策略
我把所有阴影策略压在同一个 Shader 里,用 shader_feature_local 控制变体:
1 | |
shader_feature_local→ 项目内只编译实际用到的组合,避免变体爆炸;multi_compile→ URP 全局阴影宏,必须保留以兼容内置管线流转。
7.2 调度入口:SampleCustomShadowMap
一个函数把所有路径串起来,宏决定走哪条:
GLSL
1 | |
7.3 与 Cook-Torrance BRDF 的能量守恒整合
阴影颜色不能简单 lerp 到漫反射结果之后,那样会破坏间接光与高光的能量平衡。正确做法是调制 radiance 项:
1 | |
物理含义:
shadowAttenuation = 1时光源完全照射;= 0时光源被染色透明物体过滤,剩下的 radiance 是shadowColor。这样彩色玻璃投出的有色光能正常驱动 BRDF 的 specular / diffuse 分项,而非”事后涂色”。
7.4 SRP Batcher 兼容性
这是最容易踩坑的一点。SRP Batcher 要求所有 per-material 属性集中在同一个 CBUFFER:
1 | |
踩坑要点:
- 任何
Material.SetXxx设置的属性,必须出现在 CBUFFER 内,否则 SRP Batcher 直接 break 这个材质,整个场景 Draw Call 暴增; Shader.SetGlobalXxx设置的全局参数反而不能进 CBUFFER,否则也会 break;- 在
Frame Debugger中观察 SRP Batcher 状态:”SRP Batcher = compatible” 才算通过。
八、结语:性能与表现的永恒平衡
本文方案适用边界
| 项目体量 | 推荐组合 |
|---|---|
| 移动端轻量 | Unity 内置 CSM + Dither 半透明 |
| PC 中量级 | 本文方案:彩色 RT + PCSS + Distance-Based Softness |
| PC / 主机 3A | URP CSM + 硬件 RT Shadows + Contact Shadows 缝合 |
| 风格化二次元 | 本文方案 + 透视空间 SSS + Ramp 阴影色 |
未来技术展望
- 硬件光追的降维打击:当 RTX 50 / Apple M5 系列普及后,传统 Rasterization Shadow 的所有 hack(bias、级联、漏光修补)都会被一组
TraceRayInline()直接抹平。 - SDF / Lumen / Nanite 生态:UE5 已经走出了距离场 + 虚拟化几何 + 屏幕空间探针的全新链路,Unity 这边 HDRP 的 APV(Adaptive Probe Volumes)也在追赶。
- AI 降噪:DLSS Ray Reconstruction 把 1 spp 的光追阴影补到 4K 画质,阴影开销和「能不能开光追」之间的鸿沟正在被 AI 填平。
但无论硬件怎么迭代,在指定预算下做最像物理正确的视觉这件事的本质不会变。理解 Shadow Map 双 Pass、切比雪夫、PCSS 三步曲都是计算机图形学中很有价值的事情。
- Calculate Minimal Bounding Sphere of Frustum - lxjk.github.io
- 图形引擎实战:8 级风格化级联阴影
- Unity URP 8Cascade + PCF/PCSS 阴影
- 影子传说——三种 Shadow Map 改进算法的原理与在 Unity 中的实现
- GAMES202 - 作业 1:实时阴影
- NVIDIA Whitepaper - Integrating Realistic Soft Shadows into Your Game Engine
- Contact Shadows in Unreal Engine Documentation
- 自适应 Shadow Bias 算法
- URP 体积光和彩色玻璃透光效果
- Unity 6 Render Graph System - Unity Manual
- Render Graph in URP - Unity Blog