type
Post
status
Published
date
Jun 3, 2026
slug
shadow
summary
tags
Unity
图形学
category
知识总结
icon
password
ShadowMap
ShadowMap 的基本原理
ShadowMap 是实时阴影最常见的一种实现方式。
它的核心思想是:先从光源的视角把场景“看一遍”,把可见表面的深度记录下来,再在真正渲染时拿当前像素的位置去和这张深度图比较,从而判断它有没有被遮挡。
它本质上是先生成一张记录遮挡关系的深度图,后续所有阴影判断都基于这张图完成。
基本流程
- 从光源视角渲染深度图
先把光源当成一个“摄像机”,朝场景渲染一张深度图。
这张图里记录的是:从光的方向看过去,最先碰到的表面离光源有多远。
- 得到 ShadowMap
这张深度图就是 ShadowMap。
它保存的是光照空间中的深度信息。
- 在正常渲染时处理每个像素
对屏幕上的每个像素,先拿到它对应的世界坐标。
- 把世界坐标投影回光空间
用光源的 view/projection matrix,把当前像素的世界坐标变换到光空间,得到它在光视角下的位置和深度。
- 与 ShadowMap 深度比较 (在Shader中进行)
- 如果当前深度更大,说明这个点离光更远,中间被别的东西挡住了,属于阴影中。
- 如果当前深度更小或相等,说明它能被光直接照到。
将当前像素在光空间中的深度,和 ShadowMap 中对应位置记录的深度比较:
- 得到阴影衰减值
比较结果不会只返回简单的“黑/白”,通常会得到一个 shadow attenuation 值,用来控制光照被削弱的程度。
ShadowMap 的优点
- 实现直接,实时阴影的基础方案。
- 和光源绑定,适合表达“光照被遮挡”的关系。
- 可以配合滤波、PCF、bias、cascade 等方法提升质量。
- 对于方向光、点光、聚光都可以扩展使用。
ShadowMap 的局限
- 分辨率有限,容易出现锯齿、走样、摩尔纹。
- 深度精度不足时容易出现 shadow acne、peter panning。
- 阴影质量受 shadow map 尺寸和投影范围影响很大。
- 如果阴影覆盖范围太大,同样分辨率下细节会变差。
Cascade ShadowMap
ScreenSpace Shadow
ScreenSpaceShadow 解决什么问题
它解决的是:
不要让每个材质 shader 都重复计算主光阴影,而是先在屏幕空间统一算一次,再复用结果。
普通方式下,每个接收阴影的 shader 都可能执行:
world position → light space → sample ShadowMap → compare depth
而 ScreenSpaceShadow 把这一步提前到一个全屏 pass 中完成。
后续 shader 只需要采样一张屏幕空间阴影图。
ScreenSpaceShadow 的基本原理
ScreenSpaceShadow 是一种把阴影结果提前计算到屏幕空间贴图中的做法。它不是替代 ShadowMap,而是在已有 ShadowMap 的基础上,把当前屏幕上每个像素的阴影结果统一算出来并缓存下来。
它的核心思想是:先根据屏幕深度重建每个像素的世界坐标,再用这个世界坐标去查询 ShadowMap,最后把阴影结果写入一张屏幕空间阴影图。
基本流程
- 准备 ShadowMap 和屏幕深度图
首先仍然需要正常生成 ShadowMap。
同时,渲染管线需要有一张 camera depth texture,也就是从相机视角记录的屏幕深度图。
- 执行全屏阴影计算
使用一个 full-screen pass 遍历屏幕上的每个像素。
- 根据屏幕深度重建世界坐标
对每个屏幕像素,读取 camera depth。
通过屏幕 UV、深度值和相机矩阵,可以反推出该像素对应的世界空间位置。
- 将世界坐标投影到光空间
和普通 ShadowMap 采样一样,把这个世界坐标乘以光源的 shadow matrix,得到 shadow coord。
- 查询 ShadowMap
用 shadow coord 去采样 ShadowMap,比较当前点深度和光源视角下记录的深度,得到该像素是否处于阴影中。
- 写入屏幕空间阴影图
把阴影判断结果写入一张屏幕大小的 texture。
这张 texture 记录的是“当前相机画面中每个像素的阴影衰减值”。
- 后续 shader 直接采样结果
后续物体 shader 不需要再各自查询 ShadowMap,只需要根据屏幕 UV 读取这张 ScreenSpaceShadow 结果图。
ScreenSpaceShadow 的特点
- 它依赖屏幕深度图,因此只对当前相机可见的像素有效。
- 它通常仍然依赖 ShadowMap,ShadowMap 是阴影数据源。
- 它输出的是“最终阴影结果”,不是光空间深度。
- 它适合统一主光阴影结果,方便多个 shader 复用。
- 它也方便把多种阴影来源合并成一张统一结果图。
和 ShadowMap 的关系
二者不是互相替代,而是前后关系:
ShadowMap:从光源视角记录遮挡深度
ScreenSpaceShadow:从相机视角计算并缓存当前屏幕像素的阴影结果
优点
- 后续 shader 不需要重复查询 ShadowMap。
- 方便统一主光阴影的采样方式。
- 方便合并多种阴影结果。
- 对复杂材质较多的场景,可以减少重复阴影计算。
- 接收端逻辑更简单。
局限
- 只对当前屏幕可见像素有效。
- 依赖 camera depth texture。
- 透明物体、特殊深度写入对象需要额外处理。
- 分辨率受屏幕分辨率影响。
- 不适合表达屏幕外或未写入深度对象的阴影接收关系。
Per Object Shadow
PerObject Shadow 解决了什么问题
PerObjectShadow 主要解决的是:普通全局 ShadowMap 在角色类对象上精度不够、控制不够细、表现不稳定的问题。
更具体地说,它解决了这些情况:
- 大范围 ShadowMap 细节不够 全局 ShadowMap 要覆盖整个场景,分辨率会被摊薄。 对于角色、脸部、头发这类小尺度细节,阴影容易变糊、锯齿明显,或者边缘不稳定。
- 重要对象需要单独控制阴影质量 有些对象比环境更重要,例如主角、Boss、近景角色。 它们需要更高精度、更近距离、更可控的阴影,而不是和整个场景一起平均分配阴影资源。
Per-Object Shadow 基本原理
Per-Object Shadow 是一种为特定物体单独生成阴影图的做法。它不依赖整场景统一的 shadow map,而是针对重要对象,例如角色、Boss、主角道具等,单独计算一套光空间投影,并生成更高精度、更可控的局部阴影。
它的核心思想是:
为重要的Object生成一张高精度的ShadowMap,再在shader中采样这张ShadowMap
基本流程
- 确定需要投影的物体
首先选择需要独立阴影的对象。通常不会对所有场景物体都使用 Per-Object Shadow,而是只用于视觉上重要、离相机较近、需要高质量阴影的对象。
- 计算物体包围范围
根据物体的 bounds 或 AABB,得到该物体在世界空间中的大致范围。这个范围用于决定 shadow map 应该覆盖多大的区域。
- 根据光源方向建立光空间
以光源方向为观察方向,为该物体建立一套 light view matrix。
对于方向光,可以直接使用主光方向;对于自阴影,也可以混合视线方向,让阴影在角色表面更加稳定和可控。
- 计算局部正交投影
将物体包围盒转换到光空间,计算能刚好包住该物体的投影范围。然后生成一套 orthographic projection matrix。
这样得到的 shadow map 只覆盖这个物体附近的小范围,而不是整个场景,因此同样分辨率下精度更高。
- 渲染 Per-Object ShadowMap
使用该物体专门的 shadow caster pass,从光源视角把物体渲染到一张深度图中。
如果有多个物体,可以把多张局部 shadow map 打包到同一张 atlas 中,每个物体占用一个 tile。
- 上传采样数据
渲染完成后,需要把以下数据传给 shader:
ShadowMap / Shadow Atlas
每个物体的 shadow matrix
每个物体在 atlas 中的 tile 区域
每个物体的 ID 或索引
ShadowMap 尺寸和采样偏移
- 在 shader 中采样阴影
在物体前向渲染或屏幕空间阴影阶段,将世界空间坐标转换到对应的 shadow map 坐标:
world position → shadow matrix → shadow coord
然后在 shadow map 中比较深度,得到该点是否处于阴影中。
Scene Shadow 与 Self Shadow
Per-Object Shadow 常见会拆成两类使用方式:
Scene Shadow
用于表现物体投射到场景或其他接收面上的阴影。
它通常使用真实主光方向生成 shadow map,然后在场景或屏幕空间中采样。
特点是:
- 光照方向真实
- 适合角色投射到地面、墙面或场景物体
- 可以多个物体阴影共同影响同一个接收点
Self Shadow
用于表现物体自身表面的阴影。
它不一定完全使用主光方向,有时会混合视线方向或做额外偏移,以减少自阴影噪点、穿插、背面错误和视觉不稳定。
更偏向角色表面表现
- 通常只采样当前物体自己的 shadow map
- 可以根据角色需求调整 bias 和投影方向
- 更适合 NPR 或风格化角色`
优点
- 局部范围小,阴影精度高。
- 可以只给重要对象使用,节省性能。
- 每个物体可以有独立的投影范围、分辨率和 bias。
- 可以区分场景投影和自阴影,方便做风格化控制。
- 不受全局 shadow map 分辨率和 cascade 分配影响太大。
缺点
- 需要额外的 shadow pass。
- 多个物体会增加 draw call 和 shadow map atlas 管理成本。
- shader 需要额外采样 shadow map。
- 需要处理 ID、tile、矩阵数组、bias、边缘裁剪等问题。
- 如果物体很多,需要做裁剪和优先级筛选。


