想象你是一位舞台剧的灯光师,负责让舞台上的演员、道具和布景呈现最真实的光影效果。可你很快发现一个头疼的问题:当一位大个子演员站在聚光灯前,他的身后本该拖出一道修长的影子,让观众觉得“哦,他确实站在灯光下”。然而如果你不做任何特殊处理,灯光打过去,地面上干干净净,仿佛那个人是飘在空中的幽灵。这就是计算机图形学里最古老的难题之一——怎么让虚拟世界中被光照到的地方和被挡住的地方看起来不一样? 今天要讲的阴影映射(Shadow Map),正是游戏和电影里最常用的答案。它的思路,像极了一位聪明的灯光师——我们就叫她“小影”吧。小影的工作方法很朴素:演出前,她先站到大灯的位置,拿一本小本子,记下舞台上每个物体表面离灯有多远,这相当于给“光”拍了一张“距离登记照”(专业叫法是光源深度图)。正式演出时,她回到舞台边,看着刚才那片区域,对每个位置问:你现在离灯的距离,和本子上登记的一样吗?如果一样,说明你确实被光照着;如果不一样,说明你被前面的人挡住了——那里就该是影子! 整篇文章,我们就跟着小影把“登记距离 → 对账 → 画影子”这三步走完,顺便解决“影子边缘毛糙”“影子粘在墙上”这些闹心的小毛病。

岗位关联

学会阴影映射,你就掌握了现代渲染引擎中“真实感光照”的核心一环。它直接支撑着游戏、影视和 VR 场景中任何一束光下的影子,是走向资深图形工程师的必修课。

目录
  1. 可视化演示
  2. 光影基础
  3. 渲染流程
  4. 光源深度图
  5. 阴影比对
  6. 边缘抗锯齿
  7. 瑕疵修复
  8. 工程实践
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示阴影映射双Pass渲染流程、深度比对与偏移修复、以及PCF软阴影与级联阴影CSM优化过程。

本节配有交互动效(进入视口后自动加载,约 3KB)。

'+_captions.pass+'

光影基础

小影刚当上灯光师时,以为影子是“画在地面上的一块黑”。直到有一天她看到:自己站在路灯下,影子被拉得老长;她跑到篮球架后面,影子却消失了。她忽然明白——影子不是涂上去的黑漆,而是“光的路被挡住后留下的空缺”。光沿直线传播,撞上不透明的物体就停下,物体的另一侧就收不到光,于是那里成了影。

把这个直觉搬进计算机,问题就变成:屏幕上每个像素,我怎么知道它有没有被某个物体挡住通往光源的路?这就叫遮挡检测。要判断遮挡,光靠“哪个方向有光”还不够,还必须知道“离光源多远”。在图形学里,点到光源的距离有个专门的名字——深度。深度是阴影世界里最重要的度量:如果一个点离光源的距离,比“挡在它前面的东西”的距离更远,那它必然泡在阴影里。

Shadow Map 的全部智慧,就是把这个判断化整为零:先站在光源的角度,记录下场景中每个可见表面“离光源有多远”;再回到摄像机面前,逐个像素对比“你离光源的距离,和登记的距离一样吗”。小影恍然大悟:原来她需要的不是一支画笔,而是一把卷尺——这把卷尺的名字,就叫深度。

渲染流程

小影总结出一套自己的工作节奏:先登记,再对账。她绝不会在演出进行时,慌慌张张跑到灯下去量距离,那会让全场观众等得不耐烦。她习惯在彩排时就完成一切测量,正式演出只管对照本子做判断。

Shadow Map 采用的正是这套“两步走”策略,专业说法是双 Pass 渲染管线。第一个 Pass 叫深度图 Pass:把虚拟摄像机挪到光源所在的位置,朝场景看过去,把所有可见表面到光源的距离记录到一张特殊纹理里。注意,这一步完全不关心物体长什么颜色、什么花纹,只关心“谁有多远”——所以通常连颜色输出都关掉,省下大量计算。第二个 Pass 叫渲染 Pass:把摄像机放回观众所在的位置,正常渲染整个场景,但在处理每个像素的颜色时,额外向它提一个问题——“你到光源的距离,比登记簿上写的更远吗?”

如果把整个流程拍成小影的一天:上午彩排,她站在大灯底下唰唰地填完一本“光线登记簿”;下午正式演出,她坐在舞台边,手里拿着登记簿,目光扫过每一个角落,一处处对账。对上了,光就照过去;对不上,那个位置就暗下来。小影发现,这种“先准备、后判断”的方式,让整个演出又快又稳,绝不会在关键时刻掉链子。

光源深度图

登记簿要写得准,首先得站对位置。小影发现,如果她站在观众席去量“每个演员离灯多远”,准会量出一堆错误数据——因为从侧面看,远处的演员会被近处的道具挡住,她根本看不见。正确的姿势是:直接站到灯的位置,往舞台上看。灯光照向哪里,登记簿就记录哪里。

在图形学里,这一步叫作“从光源视角渲染场景”。具体要做三件事:第一,把视点放到光源处;如果光源是太阳这样的方向光,就用正交投影(光都是平行的);如果是一盏台灯这样的点光源,就用透视投影(光向四面八方散开)。第二,按这个视角把场景整个渲染一遍,但目标不是画面,而是深度——每个可见表面片元(fragment)到光源的距离。第三,把这些距离值写进一张特殊的纹理——深度纹理。在这张纹理里,同一视角下“离光源更近”的像素深度值更小,“更远”的像素深度值更大。

小影把这本登记簿想象成一张“距离写真”:照片里每块斑驳的颜色都不是颜色,而是用数字标出的远近。她特意叮嘱自己:这张写真只关心距离,不关心美丑,所以画画时连颜料都省了。有了这本登记簿,下一步的对账工作就有了可靠的依据——它就像老师手里的点名册,谁该亮、谁该暗,全写在上面。

阴影比对

正式演出开始了。小影坐在舞台侧面,手里捏着登记簿,目光扫过场上每一个位置。她嘴里念念有词:“你说你离灯 3 米?可登记簿上离灯最近的人只要 2 米——那你肯定被前面的家伙挡住了!”这句念叨,翻译成图形学的语言,就是阴影比对的核心逻辑。

对屏幕上每一个像素 P,我们做三件事:先算出 P 到光源的真实距离 \(d_L(P)\),再把它投影到光源视角的纹理坐标上,从深度图中读出那个方向上的最近登记距离 \(D_{\text{map}}(q)\)。如果“现读数”大于“登记簿旧账”,说明 P 确实被一个更近的物体挡住了光,P 就处于阴影中;否则,P 正沐浴在阳光下。

小影发现,这套“对账判影”的方法虽然朴素,却极其可靠——它不依赖任何肉眼感觉,而是用数字说话。谁亮谁暗,一比便知。她的登记簿,终于在正式演出这一刻真正派上了用场。

shadow(P)={1,dL(P)>Dmap(q)0,否则\displaystyle \text{shadow}(P) =\begin{cases}1, & d_L(P) > D_{\text{map}}(q) \\0, & \text{否则}\end{cases}
这个公式的意思是:当一个点到光源的实际距离,比深度图里记录的最近遮挡距离还要大时,就认为它被近处的物体挡住了,标记为阴影(1);否则标记为受光(0)。
  1. 计算当前光源距离把像素 P 的位置换算到光源坐标系下,求出它到光源的真实空间距离,这是对账用的“现读数”。
    dL(P)=∣P−L∣\displaystyle d_L(P) = |P - L|
  2. 查询登记深度将 P 投影到光源视角的纹理坐标 q,从深度图中读出该方向附近最近表面的登记距离,这是“登记簿旧账”。
    Dmap(q)=深度图在纹理坐标 q 处的取值\displaystyle D_{\text{map}}(q) = \text{深度图在纹理坐标 } q \text{ 处的取值}
  3. 比较并输出判定把现读数与旧账放在一起比大小,现读数更大说明像素被前方近物遮挡,输出阴影标记 1,否则输出 0。
    shadow(P)=1[dL(P)>Dmap(q)]\displaystyle \text{shadow}(P) = \mathbb{1}\left[d_L(P) > D_{\text{map}}(q)\right]

边缘抗锯齿

小影很快发现一个问题:用登记簿算出的影子,边缘总是犬牙交错,像被小狗啃过的饼干。她蹲下来仔细看,原来深度图本身就是一张由一个个像素格子组成的图像,格子越粗,边缘的“台阶感”越明显。这就叫阴影锯齿。直接提高深度图的分辨率可以缓解,但锯齿永远不可能被彻底消灭——因为无论格子多细,它终究是离散的。

图形学里对付锯齿的经典武器,是一招叫 PCF(Percentage Closer Filtering,百分比渐近滤波) 的技术。它的思路很巧妙:不再只问一个点“你被挡了吗”,而是在阴影边缘附近多问几个点——比如 3×3 的九个邻居,让每个邻居各自做一次深度比对,得到一串 0 和 1 的硬性答案,然后把这九个答案取平均。平均之后,原本非黑即白的边缘,变成了 0.2、0.5、0.8 这样的连续渐变。人眼看到这种渐变,就会脑补出一条柔和自然的阴影边缘。

小影豁然开朗:她不再依赖“一个人的证词”,而是召集一圈邻居开会投票。票数越接近一半,说明这里越靠近影子边缘;大家的意见越是一边倒,说明这里要么是亮堂堂的,要么是黑黢黢的。这一招,让影子从“锯齿瞬间”变成了“柔和渐变”,观众再也不觉得影子像被狗啃过了。

shadowsoft(P)=1N∑i=1Nshadow(Pi)\displaystyle \text{shadow}_{\text{soft}}(P) = \frac{1}{N}\sum_{i=1}^{N}\text{shadow}(P_i)
这里的 \(N\) 是采样点的个数,\(P_i\) 是当前像素周围的邻居采样位置。对所有邻居的阴影判定结果取算术平均,就得到了柔和的边缘过渡值。
  1. 选取邻域采样点在当前像素的阴影纹理坐标周围,选取 \(N\) 个邻近采样位置,作为“向一圈邻居打听”的范围。
    Pi∈N(P),i=1,2,…,N\displaystyle P_i \in \mathcal{N}(P), \quad i = 1, 2, \dots, N
  2. 逐点执行阴影判定每个采样点 \(P_i\) 都独立完成一次深度比较,得到 0 或 1 的硬性判定结果。
    si={1,dL(Pi)>Dmap(qi)0,否则\displaystyle s_i = \begin{cases} 1, & d_L(P_i) > D_{\text{map}}(q_i) \\ 0, & \text{否则} \end{cases}
  3. 加权平均融合结果把所有邻居的判定值累加后除以 \(N\),得到 0 到 1 之间的柔和阴影浓度。
    shadowsoft(P)=s1+s2+⋯+sNN\displaystyle \text{shadow}_{\text{soft}}(P) = \frac{s_1 + s_2 + \cdots + s_N}{N}

瑕疵修复

对账工作进行到一半,小影又撞上两件怪事。第一件:影子的表面出现密密麻麻的小麻点,像舞台上飘了一层灰,专业术语叫“阴影痤疮”(shadow acne)。第二件:影子有时会“飞”起来,离物体的底部有一段明显的空隙,看起来像幽灵在飘,这叫 Peter Panning(悬浮影)。

麻点的根源在深度精度:深度图里的数值是用有限位数的二进制存的,比如 0.500001 和 0.500002 这种细微差别在存储时会被“抹平”,导致深度比较时产生微小误判。本该完全暴露在光下的表面,因为舍入误差被错误地判定为“被遮挡”。解决办法是引入 Bias(偏移):在比较时,给当前点到光源的距离减去一个微小的偏移量,相当于主动给“贴着表面”的区域松绑,让误差没有机会作怪。但 Bias 必须拿捏分寸——偏移过大,真影子也会被“抬离”物体,变成悬浮影;偏移过小,麻点又卷土重来。

小影笑着说,这就像给卷尺加了一条“原谅线”——测量难免有误差,多一点宽容,画面反而干净了。但宽容也要有个度:你要是原谅得太多,连真影子都被你“原谅”掉了。她把这个教训记在本子上:bias 是皮肤药,抹少了没用,抹多了会浮肿。

shadow(P)=0  ⟺  dL(P)−bias<Dmap(q)\displaystyle \text{shadow}(P) = 0 \iff d_L(P) - \text{bias} < D_{\text{map}}(q)
这个公式表示:当当前点到光源的距离减去偏移量 bias 之后,仍然小于深度图里的登记距离,就认为该点确实暴露在光下;bias 是给深度比较留出的容错空间。
  1. 减去偏移量从当前点到光源的实际距离中,扣掉一个数值很小的 bias,给比较过程留出容错余量。
    dL′(P)=dL(P)−bias\displaystyle d'_L(P) = d_L(P) - \text{bias}
  2. 容差判定亮暗扣掉 bias 之后的距离若仍小于登记深度,说明该点确实没有被任何近物挡住,应判定为受光面。
    shadow(P)=0  ⟺  dL′(P)<Dmap(q)\displaystyle \text{shadow}(P) = 0 \iff d'_L(P) < D_{\text{map}}(q)
  3. 平衡参数避免过修bias 的取值需要与深度存储精度匹配——偏小麻点复发,偏大影子漂浮,实际操作中常按场景反复试探取中间值。
    bias≈深度存储最小分辨率c,c 为经验常数\displaystyle \text{bias} \approx \frac{\text{深度存储最小分辨率}}{c}, \quad c \text{ 为经验常数}

工程实践

日子久了,小影对“登记簿”本身也有了心得:用多大的本子、记多细的条目,直接关系到演出成本和效果。如果舞台很大,却只带一本巴掌大的小本子,远处演员的距离全挤在几个格子里,影子必然糊成一片;可如果每场小演出都扛一本百科全书那么厚的登记簿,光是翻页就累得够呛。

在 Shadow Map 的世界里,这个权衡叫分辨率与性能的平衡。深度图的分辨率越高,阴影边缘越锐利,但显存占用和带宽压力也成倍上涨。实际项目中,常见的深度图尺寸在 1024×1024 到 4096×4096 之间。为了进一步优化,工程师们还发明了级联阴影映射(CSM):把摄像机看到的视锥体从近到远切成几段,近处用高分辨率深度图,远处用低分辨率深度图,这样既保住了观众眼皮底下的精致影子,又不会让远山的影子浪费太多算力。

这套技术已经渗透进无数真实场景:游戏引擎(Unity、Unreal)里,太阳光下的建筑投影是它;电影虚拟制片里,LED 屏上实时变化的飞船投影是它;AR 应用里,虚拟沙发在地板上投出的柔影也是它。小影的最终感悟是:工具没有绝对的好坏,只有会不会因地制宜。大型歌舞剧用厚登记簿细细记,小品短剧随手一页纸——用对地方,才是真正的专业。

AI 应用场景

游戏引擎实时阴影

阶段:渲染管线集成与优化

解决的问题:传统光照模型只能回答“这个面被照亮了多少”,却无法回答“这个面是不是被其他物体挡住了”。Shadow Map 让引擎在每一帧都能实时算出墙、树、角色在阳光下的投影,玩家转动视角时影子也跟着动,虚拟世界的沉浸感因此大幅提升。

具体案例:在 Unity 中为开放世界场景启用方向光的 Shadow Map,主城建筑在午后阳光下拉出清晰的长影;切到夜晚后,路灯作为点光源为角色身边的小物件生成环绕阴影。通过调整 Shadow Distance(阴影距离)与 CSM 分割比例,实现近处影子精致、远处影子平滑淡出的层次效果。

AR 虚拟家具摆放投影

阶段:空间光感一致性处理

解决的问题:AR 应用把虚拟沙发放进真实客厅时,如果缺少阴影,物体会像“贴纸”一样浮在地板上,毫无真实感。阴影映射能根据 AR 相机估算出的主光源方向,为虚拟物体生成可信的地面投影。

具体案例:某家装 App 中,用户把虚拟书柜拖到墙角,系统先用深度传感器获取房间几何信息,再按 Shadow Map 思路渲染一张该角度的光源深度图,在地板上叠加柔和投影。用户拖动“窗户方向”滑块模拟改变光源位置时,投影的方向和长度也随之变化,仿佛那本书柜真的立在房间里。

影视虚拟制片实时预览

阶段:实时渲染与合成校验

解决的问题:影视虚拟制片中,演员站在 LED 巨幕前表演,屏幕播放虚拟场景。如果虚拟布景的光影方向与现场实拍光源不一致,画面就会穿帮。Shadow Map 被用来在 GPU 上快速生成与现场灯光方向一致的高频更新阴影,让导演在片场就能直观判断光影效果。

具体案例:某科幻片拍摄时,LED 屏上的虚拟飞船被主光照亮,引擎借助级联阴影映射为飞船实时渲染出地面投影,并且与轨道上的真实灯位方向对齐。演员、道具、虚拟背景三者的阴影方向完全统一,后期几乎不需要额外修补。

Python 演示

python54 行
import numpy as np  def build_scene(nx=80, ny=44):    scene = np.zeros((ny, nx), dtype=np.uint8)    scene[8:11, 20:48] = 1   # 第一堵墙    scene[20:34, 52:55] = 1  # 第二堵墙    scene[28:31, 65:70] = 1  # 第三堵墙    return scene  def shadow_map_two_pass(nx=80, ny=44, light=(3, 3)):    """两遍式阴影映射演示:    Pass1 从光源方向生成深度图(记录最近遮挡距离);    Pass2 逐格比对当前距离与登记深度,判定阴影。    """    scene = build_scene(nx, ny)    lx, ly = light     # ---- Pass 1: 从光源角度渲染的深度图 ----    depth_map = np.full((ny, nx), np.inf)    for y in range(ny):        for x in range(nx):            d_light = abs(x - lx) + abs(y - ly)            nearest = np.inf            for i in range(1, 50):                t = i / 50.0                px = int(round(lx + (x - lx) * t))                py = int(round(ly + (y - ly) * t))                if scene[py, px] == 1:                    nearest = t * d_light                    break            depth_map[y, x] = nearest     # ---- Pass 2: 阴影比对(加一个量为1.5的bias避免自影误判) ----    shadow = np.zeros((ny, nx), dtype=bool)    for y in range(ny):        for x in range(nx):            if scene[y, x] == 1:                continue            cur_d = abs(x - lx) + abs(y - ly)            shadow[y, x] = cur_d - 1.5 > depth_map[y, x]     # ASCII 可视化    for y in range(ny):        print(''.join(            '#' if scene[y, x] else ('~' if shadow[y, x] else '.')            for x in range(nx)        ))    return scene, shadow  if __name__ == '__main__':    shadow_map_two_pass()
目的:用纯 Python 模拟 Shadow Map 的“两遍”核心思想——先生成光源视角的深度图,再逐点比对距离判定阴影,最终输出一幅 ASCII 阴影效果图。
  1. depth_map[y, x] = nearest把沿光源方向找到的最近遮挡距离写入深度图,这就是“登记簿”。
  2. nearest = t * d_light光线步进一步步向外试探,首次撞到墙时记录下遮挡距离。
  3. cur_d - 1.5 > depth_map[y, x]当前点到光源的距离减掉 bias 后仍大于登记深度,判定为阴影。
  4. '~' if shadow[y, x] else '.'波浪号表示影子,点表示受光面,墙用 # 表示。

常见误区

误区阴影映射是不是先把场景里的物体整体描一圈黑边,然后叠加到画面上?感觉就像用画笔给影子涂颜色一样简单。

正解完全不是。阴影映射的核心是“距离对账”:它记录的是每个表面点到光源的深度,然后逐个像素比较远近,只为“光路被遮挡”的位置生成阴影。物体朝向光源的那一面该亮还是亮,绝不会被整体涂黑。

为何常见:名字里带着“阴影”二字,容易让人联想到“变暗”“涂黑”之类的直接操作,忽略了背后基于几何深度的判断逻辑。

误区把 Shadow Map 的分辨率调得越大,阴影边缘就越干净,画面质量一定直线上升,所以无脑拉满就对了。

正解提高分辨率确实能缓解锯齿,但显存带宽、GPU 填充率开销也会成倍增加。而且分辨率再高,采样仍然是离散的,边缘锯齿依然存在,通常需要配合 PCF 或级联阴影映射才能获得理想效果。

为何常见:“像素越大越清晰”是日常生活中的直觉,但在图形学里,除了清晰度还有性能预算、带宽瓶颈等硬约束。

误区既然阴影上出现麻点,那就把 Bias 拼命调大,调得越大越能消除瑕疵,反正越干净越好。

正解Bias 太大时,阴影会整体“飘”离物体底部,形成 Peter Panning 悬浮影——影子与物体脱节,看起来像幽灵。正确的做法是让 Bias 与深度精度匹配,或使用根据表面斜率自适应的动态 Bias,在“消除麻点”与“保持贴合”之间找平衡。

为何常见:麻点和悬浮影都是试错过程中交替出现的,新手看不到内在的权衡关系,容易陷入“一味调大”的极端。

误区画阴影就是把某个区域填成半透明的黑色,阴影颜色都是差不多的灰黑,没什么好讲究的。

正解阴影的浓度和颜色由多种因素决定:遮挡物的透光率、环境光强度、光源颜色等。半影区的阴影更淡,全影区更浓;彩色光源下的阴影会带有光源色调。Shadow Map 输出的其实是 0 到 1 的阴影因子,最终颜色是在光照模型里算出来的。

为何常见:日常生活中人们很少仔细观察阴影颜色,总把它当成“灰色一块”,导致忽略了阴影因子与光照模型的耦合关系。

误区只要把物体画出来,引擎就会自动把影子带上,好像影子是物体自带的属性,一次渲染就够。

正解Shadow Map 至少要两遍渲染:第一遍从光源视角渲染深度图,第二遍渲染主场景并逐像素比对。如果场景里有动态物体或灯光移动,深度图每帧都要重新生成。两遍 Pass 是它的基本骨架,少一遍都无法工作。

为何常见:用户只看得到最终画面,看不到渲染管线内部的工作过程,自然以为影子是一次渲染的“赠品”。

面试题

Q1. 阴影映射(Shadow Map)的核心思想是什么?

单选题 · easy

  • A.从摄像机视角记录颜色,渲染时叠加黑色
  • B.从光源视角记录深度,渲染时比对距离← 正确答案
  • C.从光源视角记录颜色,再复制到地面
  • D.从摄像机视角记录深度,再翻转颜色

答案:B。Shadow Map 的关键是“从光源看”:第一遍在光源处渲染深度图,第二遍在摄像机处比对距离。A、C 搞错了视角和记录内容,D 把方向完全弄反了,都不符合“先登记距离再对账”的核心思想。

Q2. 阴影比对阶段,某点被判定为阴影的条件是什么?

单选题 · easy

  • A.当前点到光源距离大于登记值← 正确答案
  • B.当前点到光源距离小于登记值
  • C.当前点到光源距离等于登记值
  • D.当前点到光源距离恒定不变

答案:A。当像素到光源的实际距离大于深度图登记的最近距离,说明它被更近的物体挡在了光路之外,应判为阴影。小影的对账逻辑正是“你说你离灯 3 米,可登记簿上最近人才 2 米——你被挡住了”。

Q3. 渲染深度图 Pass 时,通常可以关闭哪一项来节省开销?

单选题 · easy

  • A.颜色输出← 正确答案
  • B.深度测试
  • C.顶点着色器
  • D.视口裁剪

答案:A。深度图 Pass 只关心“谁离光源最近”,不关心表面长什么样,所以会关闭颜色写入,只保留深度测试与深度写入。关闭深度测试会导致深度缓冲失效,关闭顶点着色器则无法变换顶点,视口裁剪是裁剪步骤,都与节省开销的目标不符。

Q4. PCF(百分比渐近滤波)的核心操作是什么?

单选题 · mid

  • A.对深度图做高斯模糊
  • B.对阴影判定结果取平均← 正确答案
  • C.对场景颜色做锐化
  • D.对光源方向做插值

答案:B。PCF 的巧妙之处在于它模糊的不是深度图本身,而是“判定结果”:对周围多个采样点各自做一次阴影比较,再把 0/1 结果取平均,得到 0~1 的柔和过渡。对深度图做模糊会破坏距离信息,锐化和插值更是南辕北辙。

Q5. 阴影出现“悬浮”(Peter Panning)现象,最可能的原因是什么?

单选题 · mid

  • A.Bias 参数设置过大← 正确答案
  • B.Bias 参数设置过小
  • C.深度图分辨率过高
  • D.光源颜色设置过亮

答案:A。Bias 是为了消除深度精度导致的麻点而引入的容差。偏移量过小麻点复发,偏移量过大的影子会被整体“抬离”物体底部,形成悬浮感。这就像小影的原谅线画得太宽,连真影子都被“原谅”掉了。

Q6. 自阴影痤疮(阴影表面麻点)的根本成因是什么?

单选题 · mid

  • A.深度值离散化产生误差← 正确答案
  • B.场景中物体数量过多
  • C.摄像机离物体过近
  • D.光源颜色过于明亮

答案:A。深度图用有限位数的二进制存储距离值,数值被离散化后存在舍入误差。比较两个几乎相等的深度时,误差就把本该受光的表面误判成阴影。物体数量、摄像机距离、光源颜色都与深度精度无关。

Q7. 方向光(平行光)渲染 Shadow Map 时,通常使用哪种投影矩阵?

单选题 · mid

  • A.正交投影矩阵← 正确答案
  • B.透视投影矩阵
  • C.斜切投影矩阵
  • D.单位投影矩阵

答案:A。方向光的光线是平行的,没有近大远小的透视效果,所以要用正交投影来模拟,这样深度值沿光方向均匀分布。点光源才用透视投影,斜切和单位矩阵与光源投影无关。

Q8. 下列哪些方法可以缓解 Shadow Map 的边缘锯齿?

多选题 · hard

  • A.增大深度图分辨率← 正确答案
  • B.使用 PCF 多次采样← 正确答案
  • C.提高深度格式精度← 正确答案
  • D.把偏移 bias 调为 0

答案:ABC。分辨率提高让采样网格更细,是缓解锯齿的直接手段;PCF 通过采多个点平均让边缘柔和;提高深度格式精度减少了舍入误差带来的边缘抖动。把 bias 调为 0 会加剧自阴影痤疮,与锯齿缓解无关。

Q9. 关于级联阴影映射(CSM),下列说法正确的有哪些?

多选题 · extreme

  • A.视锥切分多段渲染深度图← 正确答案
  • B.近处高分辨率远处低← 正确答案
  • C.所有区域共用一张图
  • D.适合超远视距场景← 正确答案

答案:ABD。CSM 把摄像机视锥从近到远分成多段,每段单独渲染深度图,近处段用高分辨率、远处段用低分辨率,这使超远视距场景也能保持近景精细远景平滑。C 恰好说反了——CSM 的特点正是“多张图分区域”,而不是共用一张。

Q10. Shadow Map 在实际工程中需要注意哪些问题?

多选题 · hard

  • A.深度图分辨率平衡显存← 正确答案
  • B.Bias 过大会产生悬浮影← 正确答案
  • C.PCF 采样数影响性能← 正确答案
  • D.阴影颜色固定为纯黑

答案:ABC。深度图分辨率越高越清晰,但显存和带宽开销也越大;Bias 过大导致影子漂浮;PCF 采样点越多边缘越柔和但性能开销也越大。阴影颜色由光照模型和环境光决定,绝非固定纯黑——就像生活里不同环境下的影子深浅各不相同。