Skip to content

阴影

Shadow Map 原理是什么?

一句话理解

Shadow Map 的原理是:先从光源视角渲染一张深度图,再从相机视角渲染场景时,把当前点的光源空间深度和这张深度图比较。如果当前点比阴影图里记录的最近深度更远,说明它被挡住了,就在阴影里。

rendering-shadow-map-principle

第一步:从光源视角看场景

把光源当成一台相机。

如果是方向光,可以理解成从光照方向看过去。 如果是聚光灯,就像从聚光灯位置往前看。 如果是点光源,通常需要立方体阴影贴图,因为它向四周发光。

这一遍不关心颜色,只关心深度。

也就是记录:

从光源看过去,每个方向最近的物体有多远

这张深度图就是 Shadow Map

第二步:从相机视角正常渲染

相机渲染某个像素时,会得到这个像素对应的世界空间点 P

然后把 P 转到光源空间,看它在光源视角下的位置和深度。

接着拿它的深度去查 Shadow Map:

currentDepth > shadowDepth + bias

如果成立,说明从光源到 P 的路上,有一个更近的东西挡住了光。 所以 P 在阴影中。

如果不成立,说明光源能直接照到 P。 所以 P 是亮的。

为什么它叫 Shadow Map

因为阴影不是直接画出来的。 它本质是一张“从光源视角看到的深度贴图”。

你可以把它理解为:

光源先拍一张“谁离我最近”的深度照片;

相机渲染时,每个点都问这张照片:“我是不是光源能看到的最近点?”

如果不是,说明被挡住了。

常见问题 1:Shadow Acne

阴影痤疮,也就是表面出现很多黑色条纹或斑点。

原因通常是深度精度问题: 当前点和 Shadow Map 里的深度非常接近,浮点误差会让表面误以为自己被自己挡住。

常见解决:

增加 Bias

使用 Normal Bias

提高阴影图精度;

调整光源投影范围。

常见问题 2:Peter Panning

如果 Bias 太大,阴影会和物体分离。 看起来像物体飘起来了,阴影没有贴住脚底。

所以 Bias 不是越大越好。 它是在 Shadow AcnePeter Panning 之间找平衡。

常见问题 3:锯齿和模糊

Shadow Map 本质是一张贴图。 如果阴影图分辨率太低,或者一个阴影图覆盖范围太大,就会出现锯齿、闪烁、阴影糊。

解决思路:

提高 Shadow Map 分辨率;

缩小阴影覆盖范围;

使用 PCF 过滤软化阴影边缘;

方向光使用 CSM 级联阴影。

CSM 是什么

CSM 是 Cascaded Shadow Map,级联阴影贴图。

方向光照射范围很大,如果只用一张 Shadow Map,近处阴影会很糊。 CSM 会把相机视锥分成几段:

近处一张高精度 Shadow Map;

中距离一张;

远处一张低精度。

这样近处阴影清楚,远处也能兼顾性能。

面试高分说法

TIP

Shadow Map 是一种基于深度比较的阴影技术。它分两遍:第一遍从光源视角渲染场景,只记录最近深度,得到 Shadow Map;第二遍从相机视角渲染时,把当前像素的世界坐标转换到光源空间,取出它在光源视角下的深度,并和 Shadow Map 中保存的最近深度比较。如果当前深度更大,说明光源到该点之间有遮挡物,该点就在阴影中。它的典型问题有 Shadow Acne、Peter Panning、阴影锯齿和分辨率不足,常用 Bias、Normal Bias、PCF、提高分辨率和 CSM 来优化。

阴影痤疮是什么?如何缓解?

一句话理解

阴影痤疮 Shadow Acne 是 Shadow Map 里很常见的问题:物体表面本来应该被光照到,但因为深度比较误差,被误判成“在阴影里”,于是表面出现黑色条纹、斑点、闪烁。

rendering-shadow-acne-mitigation

为什么会产生

Shadow Map 是两遍计算:

第一遍,从光源视角渲染深度图,记录每个位置离光源最近的深度。

第二遍,从相机视角渲染当前点,再把当前点转换到光源空间,和 Shadow Map 里的深度比较。

理论上,同一个表面点的深度应该一样。 但实际会有误差,比如:

深度贴图精度有限;

Shadow Map 分辨率有限;

光源投影和相机投影采样不完全一致;

斜面上一个阴影像素覆盖多个实际表面点;

浮点计算有误差。

于是当前点的深度可能略微大于 Shadow Map 里记录的深度:

currentDepth > shadowDepth

渲染器就会误以为当前点被挡住了,于是给它画上阴影。 这就是“自己阴影自己”。

如何缓解 1:Depth Bias

最常见办法是加深度偏移。

比较时不直接写:

currentDepth > shadowDepth

而是写成:

currentDepth > shadowDepth + bias

这样可以给误差留一点余量,减少表面误判。

如何缓解 2:Normal Bias

Normal Bias 是沿着表面法线方向,把采样点稍微推开一点。

它对斜面和曲面特别有用。 因为斜面上 Shadow Map 的一个像素可能覆盖一大片真实表面,很容易出现深度误差。

如何缓解 3:Slope-Scaled Bias

斜率越大的表面越容易出现深度误差。 所以有些渲染管线会根据表面斜率动态增加 Bias。

表面越斜,Bias 越大; 表面越平,Bias 越小。

这样比固定 Bias 更合理。

如何缓解 4:提高阴影精度

可以通过这些方式减少误差:

提高 Shadow Map 分辨率;

缩小光源投影范围;

减少 Shadow Distance;

方向光使用 CSM 级联阴影;

近处阴影用更高精度,远处阴影用较低精度。

注意:Bias 不是越大越好

Bias 太小,会有 Shadow Acne。 Bias 太大,会有 Peter Panning。

Peter Panning 就是阴影和物体分离,看起来像物体飘起来了。

所以调阴影经常是在这两个问题之间找平衡。

Unity 里怎么调

Unity 中常见调参方向:

Bias:减少表面自阴影。

Normal Bias:沿法线方向偏移,缓解斜面问题。

Shadow Resolution:提高阴影贴图分辨率。

Shadow Distance:减少阴影覆盖距离,提高有效精度。

Cascades:方向光使用级联阴影,让近处更清晰。

面试高分说法

TIP

阴影痤疮是 Shadow Map 深度比较误差导致的自阴影问题。Shadow Map 第一遍从光源视角记录最近深度,第二遍从相机视角把当前点转换到光源空间后比较深度。由于深度精度、分辨率、投影采样和斜面误差,同一个表面的当前深度可能略大于阴影图深度,于是被误判为在阴影中,表面就出现黑色条纹或闪烁。常见缓解方式是加 Depth Bias、Normal Bias、Slope-Scaled Bias,提高阴影图分辨率,缩小阴影范围,使用 CSM。但 Bias 过大会导致 Peter Panning,所以需要在自阴影和阴影脱离之间调平衡。

Peter Panning 是什么?

一句话理解

Peter Panning 是阴影贴图里的一个问题:阴影和投射阴影的物体分离了,看起来像物体悬空。它通常是因为 Shadow BiasNormal Bias 调得太大。

rendering-peter-panning-explained

为什么会出现

Shadow Map 判断一个点是否在阴影里时,会做深度比较:

currentDepth > shadowDepth + bias

bias 是为了缓解阴影痤疮 Shadow Acne。 因为不加偏移时,表面可能由于深度误差自己阴影自己,出现黑斑和条纹。

但是如果 bias 太大,就会把阴影整体“推远”。 靠近物体底部的接触阴影会被吃掉,阴影不再贴着物体,看起来就像物体飘起来。

它和 Shadow Acne 的关系

这两个问题是一对矛盾:

Bias 太小:容易出现 Shadow Acne,表面有黑色条纹。

Bias 太大:容易出现 Peter Panning,阴影和物体分离。

所以调阴影不是简单把 Bias 拉大,而是在两者之间找平衡。

如何缓解

第一,降低 Depth Bias。 这是最直接的方法,让阴影重新贴近物体。

第二,调整 Normal Bias。 Normal Bias 会沿法线方向偏移采样点,能缓解自阴影,但过大也会让阴影分离。

第三,提高阴影图精度。 比如提高 Shadow Map 分辨率,减少深度误差。

第四,缩小阴影覆盖范围。 方向光如果 Shadow Distance 太大,一张阴影图要覆盖很远范围,单位面积精度就下降,更容易需要较大的 Bias。

第五,使用 CSM。 级联阴影可以让近处阴影用更高精度,减少对大 Bias 的依赖。

第六,使用 Contact Shadows。 接触阴影可以补回角色脚底、物体底部、墙角这些近距离阴影,缓解悬空感。

Unity 里怎么调

如果看到阴影漂移、脚底没贴住,可以尝试:

降低 Light 的 Bias

降低或微调 Normal Bias

提高 Shadow Resolution

减少 Shadow Distance

开启或调整 Cascades

使用 Contact Shadows 补接触区域。

但每次只改一个参数,不然很难判断是哪一个参数起作用。

面试高分说法

Peter Panning 是 Shadow Map 中由于阴影 Bias 过大导致的阴影脱离现象。为了避免 Shadow Acne,我们会在深度比较时加入 Bias,但 Bias 太大会让靠近物体接触处的阴影被错误判定为受光,结果阴影和物体之间出现空隙,看起来像物体悬浮。解决思路是减小 Depth Bias 或 Normal Bias,同时提高阴影分辨率、缩小阴影范围、使用 CSM 提升近处精度,必要时用 Contact Shadows 补接触阴影。核心是平衡 Shadow Acne 和 Peter Panning。

PCF 是什么?

一句话理解

PCF 是 Percentage-Closer Filtering,百分比渐近过滤。它是 Shadow Map 常用的阴影过滤方法:不只采样一次阴影图,而是在当前点周围采样多个点,每个点都做深度比较,最后把比较结果平均,让阴影边缘更柔和。

rendering-pcf-explained

没有 PCF 时

普通 Shadow Map 通常只查一次深度:

currentDepth > storedDepth ? inShadow : lit

结果只有两种:

在阴影里;

不在阴影里。

所以阴影边缘很硬,而且 Shadow Map 分辨率不够时,很容易看到锯齿。

PCF 做了什么

PCF 会在当前阴影坐标附近取多个采样点,比如 3x35x5

每个采样点都做一次深度比较:

这个采样点认为当前像素在阴影里,记为 1

这个采样点认为当前像素被光照到,记为 0

最后把这些结果平均。

比如 9 个采样点里:

3 个认为在阴影里;

6 个认为被光照到;

最终阴影强度就是 3 / 9 = 0.33

这样边缘就不是纯黑或纯亮,而是有过渡。

重点:PCF 不是直接模糊深度图

这是面试加分点。

PCF 不是把 Shadow Map 的深度值直接平均。 它是:

先多次采样深度;

每个采样点分别做深度比较;

再平均比较后的阴影结果。

所以它过滤的是“可见性结果”,不是简单过滤深度值。

为什么它能让阴影变柔

硬阴影边缘附近,有的采样点落在阴影区域,有的采样点落在受光区域。 平均以后就得到一个中间值。

越靠近阴影内部,采样结果越多是阴影。 越靠近亮区,采样结果越多是受光。 中间区域就形成了柔和过渡。

PCF 的代价

PCF 要多次采样 Shadow Map。

3x3 是 9 次采样;

5x5 是 25 次采样;

采样范围越大,阴影边缘越柔,但 GPU 成本也越高。

所以移动端不能无脑开很大的 PCF kernel。

PCF 的局限

PCF 只是让阴影边缘平滑。 它不是真正物理正确的软阴影。

真实软阴影的半影大小和光源大小、遮挡物距离、接收面距离有关。 普通 PCF 的软边宽度通常比较固定,不一定符合真实物理。

更高级的方案比如:

PCSS:根据遮挡物距离估算半影大小;

VSMEVSM:用不同方式支持更好的过滤;

接触阴影:补近距离细节。

面试高分说法

NOTE

PCF 是 Shadow Map 的一种过滤方法,用来缓解阴影边缘锯齿和过硬的问题。它不会简单平均 Shadow Map 的深度,而是在当前阴影坐标附近采样多个深度点,对每个采样点分别做深度比较,得到多个 0 或 1 的可见性结果,最后求平均。这个平均值作为阴影强度,因此阴影边缘会从亮到暗逐渐过渡。PCF 的采样核越大,阴影越柔,但纹理采样成本越高。它能改善视觉效果,但不是真正物理正确的软阴影。

CSM 级联阴影是什么?

CSM 全称是 Cascaded Shadow Maps,中文一般叫级联阴影贴图。它主要用来解决方向光阴影范围很大时,近处阴影不清楚的问题。

rendering-csm-cascaded-shadow-map

为什么需要 CSM? 如果只用一张 Shadow Map 覆盖整个摄像机可见范围,那么这张图既要负责近处,又要负责远处。

问题是:Shadow Map 分辨率是有限的。 比如一张 2048 x 2048 的阴影图,如果覆盖 200 米范围,近处一小块地面分到的像素就很少,阴影边缘会糊、锯齿明显。

所以 CSM 的核心思想是:

把摄像机视锥按距离切成几段,每一段单独生成一张 Shadow Map。

近处范围小,但用同样分辨率,所以精度高。 远处范围大,但玩家不太容易看清细节,所以精度低一点也能接受。

流程怎么走?

  1. 摄像机看到一个视锥范围。
  2. 按深度切成 Near、Middle、Far 几段。
  3. 每一段从光源方向重新渲染一张 Shadow Map。
  4. 渲染物体时,根据当前像素离摄像机的距离,选择对应的 Cascade。
  5. 用这一段的 Shadow Map 判断当前点是否在阴影里。

一句话讲给面试官:

CSM 本质是在按距离重新分配 Shadow Map 精度,把更多阴影分辨率留给玩家最容易看见的近处区域。

代价是什么? CSM 不是免费的。级联越多,阴影质量越好,但代价也越高。

常见代价有:

  • 方向光需要渲染多张 Shadow Map。
  • 阴影投射物要被多次裁剪和绘制。
  • 显存占用增加。
  • Shader 中要判断当前像素属于哪个 Cascade。
  • 级联交界处可能出现接缝。

Unity 里怎么理解? Unity 的方向光阴影经常会用 CSM。你会看到这些设置:

  • Shadow Distance:阴影最远显示多远。
  • Cascades:使用 2 级还是 4 级阴影。
  • Cascade Splits:近中远几段怎么分。
  • Shadow Resolution:阴影贴图分辨率。
  • Bias / Normal Bias:缓解阴影痤疮和 Peter Panning。

面试里可以补一句:

CAUTION

CSM 调得好不好,关键不是级联越多越好,而是 Shadow Distance、Cascade Split、Bias 和分辨率之间的平衡。

点光源阴影为什么更贵?

点光源阴影为什么更贵?

因为点光源是向四面八方发光的。 它不像聚光灯只照一个锥形方向,也不像方向光可以按一个方向投影。点光源要判断物体在它周围任意方向是否被遮挡,所以通常要生成一张 Cubemap Shadow Map

rendering-point-light-shadow-cost

底层原理

普通阴影贴图的思路是:

先从光源视角渲染一张深度图,然后主相机渲染时,把当前像素拿去和阴影图里的深度比较。

但点光源的问题是:

它没有一个固定方向。 一个点光源可能往前、后、左、右、上、下都照到物体。

所以点光源阴影一般要渲染 6 个方向

  • +X
  • -X
  • +Y
  • -Y
  • +Z
  • -Z

也就是把点光源当成中心,向六个方向各渲染一次深度,组成一张 Cubemap。

为什么贵?

第一,渲染次数更多。 一个聚光灯阴影通常渲染 1 张 Shadow Map。 一个点光源阴影通常要渲染 6 张。

所以同样一批投影物,如果被一个点光源照到,可能要多画 6 次。

第二,显存更高。 因为它不是保存一张 2D 深度图,而是保存 Cubemap 的 6 个面。

第三,Shader 采样更复杂。 普通阴影可以用二维 UV 去采样 Shadow Map。 点光源阴影要根据“从点光源指向当前像素的方向”去采样 Cubemap。

第四,过滤更贵。 如果还要做 PCF 软阴影,采样次数会继续增加。

面试高分回答

点光源阴影贵的核心原因是: 点光源是全方向光源,不能用一张普通 2D Shadow Map 表达完整阴影,通常需要渲染 Cubemap 的 6 个面,相当于从光源位置向六个方向各渲染一次场景深度,因此 DrawCall、显存、采样和过滤成本都更高。

Unity 里怎么优化?

常见做法是:

  • 少开实时点光源阴影。
  • 缩小 Range
  • 降低 Shadow Resolution
  • 只让重要物体投射阴影。
  • Culling Mask 控制哪些层参与阴影。
  • 静态灯光尽量烘焙。
  • 用假阴影、Blob Shadow、Light Probe 替代部分实时阴影。

一句话记忆:

点光源阴影贵,不是因为点光源本身一定贵,而是因为“带实时阴影的点光源”通常要做六个方向的阴影渲染。

烘焙阴影和实时阴影区别是什么?

烘焙阴影和实时阴影区别是什么?

一句话: 烘焙阴影是提前算好,运行时直接用;实时阴影是运行时根据灯光和物体位置动态计算。

rendering-baked-vs-realtime-shadows

烘焙阴影是什么?

烘焙阴影就是在编辑器里提前把光照和阴影算出来,然后存到贴图里,比如 Lightmap

运行时物体渲染时,不需要再重新计算这些静态阴影,只要采样贴图就行。

适合:

  • 地面
  • 建筑
  • 静态场景道具
  • 固定灯光
  • 不会移动的阴影关系

它的优点是性能好,阴影可以很柔和,也可以包含间接光效果。 缺点是动态性差,物体或灯光动了,烘焙结果不会自动跟着变。

实时阴影是什么?

实时阴影是在游戏运行时计算的。

底层大概是:

  1. 从光源视角渲染一张 Shadow Map
  2. 里面记录光源能看到的最近深度。
  3. 主相机渲染物体时,把当前像素转换到光源空间。
  4. 拿当前像素深度和 Shadow Map 里的深度比较。
  5. 如果当前像素比记录的深度更远,说明它被挡住了,就在阴影里。

适合:

  • 主角
  • 怪物
  • 移动物体
  • 技能物体
  • 动态灯光
  • 昼夜变化
  • 可破坏场景

优点是灵活,物体怎么动,阴影就怎么变。 缺点是性能更贵,容易出现锯齿、闪烁、阴影痤疮、Peter Panning 等问题。

面试高分回答

IMPORTANT

烘焙阴影和实时阴影最大的区别在于阴影结果生成的时机

烘焙阴影是在编辑器阶段预计算,把静态光照和阴影写入 Lightmap,运行时只采样贴图,所以性能好,但无法响应动态物体和动态灯光变化。

实时阴影是在运行时通过 Shadow Map 动态计算,可以跟随角色、灯光和物体变化,但会增加额外渲染 Pass、DrawCall、显存和采样成本。

项目里通常怎么用?

比较常见的方案是:

静态场景用烘焙阴影,动态角色用实时阴影。

比如:

  • 地面、墙体、建筑:烘焙。
  • 主角脚下阴影:实时或假阴影。
  • 怪物阴影:近处实时,远处关闭或用简化阴影。
  • 室内固定灯:烘焙。
  • 战斗技能光效:少量实时,更多用特效模拟。

Unity 里相关概念

你可以这样理解:

  • Baked Light:只参与烘焙,不做实时光照。
  • Realtime Light:运行时实时照明和阴影。
  • Mixed Light:静态物体可以烘焙,动态物体还能受到部分实时影响。
  • Lightmap:存烘焙光照和阴影结果。
  • Light Probe:给动态物体采样周围的烘焙光照。
  • Shadowmask:混合模式里常见,用来保存静态阴影遮罩。

一句话记忆

烘焙阴影省性能但不灵活;实时阴影灵活但更贵。

Contact Shadow 是什么?

Contact Shadow 中文可以理解成接触阴影。 它专门用来补物体和表面接触处的细小阴影,比如角色脚底、箱子底部、墙角缝隙。

rendering-contact-shadow-explained

为什么需要它?

普通 Shadow Map 负责大范围阴影,但它经常会因为:

  • 阴影贴图分辨率不够
  • Bias 设置太大
  • 阴影距离太远
  • 物体离接触面太近
  • 阴影过滤导致边缘变软

导致物体脚底或底部看起来“飘起来”。

Contact Shadow 就是补这个问题的: 它不负责大阴影,而是补近距离接触处的小黑影。

底层原理

很多引擎里的 Contact Shadow 是一种屏幕空间技术

大概流程是:

  1. 当前像素已经有深度信息。
  2. 根据光源方向,从当前像素附近做一小段射线步进。
  3. 每走一步,就和屏幕深度图比较。
  4. 如果发现前面有更近的遮挡物,就认为这个点被挡住。
  5. 给这个像素加一点阴影。

所以它常见特点是:

只查很短距离,只补接触附近。

和 Shadow Map 的区别

Shadow Map 是从光源视角看整个场景,适合做大范围阴影。

Contact Shadow 是从屏幕空间里补细节,适合做小范围接触阴影。

面试可以这么说:

Shadow Map 解决“有没有大阴影”,Contact Shadow 解决“接触处够不够贴地”。

和 AO 的区别

AO 是环境光遮蔽,主要表现缝隙、角落、凹陷处光线不容易进入。它通常不强依赖某个具体光源方向。

Contact Shadow 更像是某个光源造成的局部遮挡,它和光照方向有关。

简单记:

  • AO:环境遮蔽,偏全局氛围。
  • Contact Shadow:接触阴影,偏灯光方向。
  • Shadow Map:真正的投影阴影,负责大范围遮挡。

它有什么缺点?

因为很多 Contact Shadow 是屏幕空间算法,所以有屏幕空间的通病:

  • 屏幕外的物体算不到。
  • 被其他物体挡住、没写入深度的物体算不到。
  • 距离太长容易出现噪点、断裂、穿帮。
  • 开太多光源的 Contact Shadow 会影响性能。
  • 移动端要谨慎使用。

Unity 里怎么调?

常见调参重点是:

  • Length:接触阴影检测距离。
  • Distance:多远范围内启用。
  • Opacity:阴影强度。
  • Sample Count:采样数量,越高越贵。
  • 哪些光源开启 Contact Shadow。

Length 太短,效果不明显。 Length 太长,容易把屏幕空间缺陷暴露出来。

面试高分回答

Contact Shadow 是一种补充阴影细节的技术,主要用来增强物体和地面、墙面接触处的短距离阴影。它通常基于屏幕空间深度,从当前像素沿光照方向做短距离检测,用来弥补 Shadow Map 分辨率、Bias 和过滤造成的接触阴影丢失。

它不能替代 Shadow Map,因为它只适合近距离细节,而且有屏幕空间限制。项目里一般是: 大范围阴影用 Shadow Map,接触细节用 Contact Shadow,静态场景尽量用烘焙。

阴影距离如何影响性能?

Shadow Distance 可以理解成: 离摄像机多远以内的物体,才需要显示实时阴影。

距离越大,参与实时阴影计算的物体越多,Shadow Map 覆盖范围越大,所以性能会下降。

rendering-shadow-distance-performance

底层为什么会变贵?

实时阴影通常不是主相机直接画出来的。 它会先从光源视角渲染一遍场景,生成 Shadow Map

所以阴影距离变大后,会发生几件事:

  1. 更多物体进入阴影范围。
  2. 更多物体需要参与阴影剔除。
  3. 更多物体要执行 ShadowCaster Pass
  4. Shadow Map 要覆盖更大的世界范围。
  5. 同样分辨率下,阴影精度会下降。

所以它影响的不只是 GPU,也会影响 CPU。

CPU 方面

CPU 要做:

  • 找出哪些物体在阴影范围内。
  • 判断哪些物体会投射阴影。
  • 做阴影渲染的剔除。
  • 提交阴影渲染命令。

Shadow Distance 越远,进入这个范围的物体越多,CPU 这部分工作就越重。

GPU 方面

GPU 要把参与投影的物体渲染到 Shadow Map 里。

也就是说,很多物体不只在主相机里画一次,还要在阴影 Pass 里再画一次。

如果是方向光加 CSM,还可能要渲染多个 Cascade。 如果是点光源阴影,那成本更高,因为可能要渲染 Cubemap 的 6 个面。

画质方面

阴影距离拉大,不一定只会变卡,还可能变糊。

因为 Shadow Map 分辨率有限。 比如同样是 2048 x 2048

  • 覆盖 30 米,单位面积像素更多,阴影更清楚。
  • 覆盖 150 米,单位面积像素更少,阴影更糊。

这就是为什么远距离阴影经常锯齿、闪烁、游泳感明显。

Unity 里怎么调?

常见参数有:

  • Shadow Distance:阴影显示距离。
  • Shadow Resolution:阴影贴图分辨率。
  • Cascades:方向光级联数量。
  • Cascade Splits:级联分布。
  • Bias / Normal Bias:修正阴影痤疮和 Peter Panning。
  • Cast Shadows:物体是否投射阴影。
  • Receive Shadows:物体是否接收阴影。

项目优化思路

移动端一般不要把阴影距离拉太远。

常见做法:

  • 主角附近保留实时阴影。
  • 远处关闭实时阴影。
  • 静态场景用烘焙阴影。
  • 小物件关闭 Cast Shadows
  • 远处角色用假阴影。
  • 降低阴影分辨率。
  • 缩小灯光影响范围。
  • 控制实时阴影灯光数量。

面试高分回答

TIP

阴影距离越大,实时阴影系统需要处理的空间范围越大,更多物体会参与阴影剔除和 ShadowCaster Pass,CPU 提交和 GPU 深度绘制成本都会增加。对于方向光 CSM 来说,Shadow Distance 还会影响级联阴影覆盖范围,同样分辨率下覆盖越远,单位面积阴影精度越低,所以会更容易出现阴影模糊、锯齿和闪烁。

一句话记忆:

Shadow Distance 越远,实时阴影越贵,而且不一定更清楚,反而可能更糊。

移动端阴影如何优化?

移动端阴影优化的核心不是“把阴影调低一点”这么简单,而是:

先减少实时阴影的参与者,再用烘焙、假阴影、分档策略补画面效果。

rendering-mobile-shadow-optimization

为什么移动端阴影贵?

实时阴影通常要先从光源视角再渲染一遍场景,生成 Shadow Map。 这意味着很多物体不只在主相机里画一次,还要在阴影 Pass 里再画一次。

如果是方向光,还可能有 CSM 级联阴影。 如果是点光源阴影,可能要渲染 Cubemap 的 6 个面,成本更高。

所以移动端最怕:

  • 多个实时阴影灯光。
  • 阴影距离太远。
  • Shadow Caster 太多。
  • 阴影分辨率太高。
  • 软阴影、Contact Shadow、PCF 采样太多。
  • 大量动态角色同时投射实时阴影。

第一优先级:减少实时阴影灯光

移动端通常只保留一个主方向光的实时阴影。

比如:

  • 太阳光可以开实时阴影。
  • 点光源、技能光、火把光尽量不开实时阴影。
  • 装饰灯用烘焙或假光效。
  • 战斗特效用贴图、粒子、Bloom 表现,不靠实时阴影。

面试可以说:

移动端实时阴影灯光数量要严格控制,尤其要避免多个带阴影的点光源。

第二优先级:静态场景用烘焙

地面、墙、建筑、固定道具、固定灯光,尽量用 Lightmap 烘焙。

运行时只采样贴图,比实时算阴影便宜很多。

常见组合是:

  • 静态场景:烘焙阴影。
  • 主角:实时阴影。
  • 怪物:近处实时,远处关闭。
  • 大量小怪:假阴影。
  • 动态物体:用 Light Probe 接收烘焙光。

第三优先级:缩短 Shadow Distance

Shadow Distance 越远,参与阴影计算的物体越多。 而且同样分辨率要覆盖更大范围,阴影还会更糊。

移动端一般只保留玩家附近阴影:

  • 近处角色和地面有阴影。
  • 中远距离逐渐淡出。
  • 远景使用烘焙阴影或无阴影。
  • 用雾效、景深、低细节模型隐藏远处阴影缺失。

第四优先级:减少投影物数量

不是所有物体都需要投射阴影。

可以关闭这些物体的 Cast Shadows

  • 小石子
  • 灌木
  • 碎片
  • 子弹
  • 小特效
  • 远处 NPC
  • 远处装饰物

还可以用 LayerCulling Mask 控制哪些对象参与阴影。

第五优先级:降低阴影质量参数

常见可调项:

  • 降低 Shadow Resolution
  • 减少 Cascade Count
  • 缩短 Shadow Distance
  • 关闭软阴影
  • 减少 PCF 采样
  • 关闭 Contact Shadow
  • 调整 Bias 和 Normal Bias
  • 降低远处阴影质量

移动端一般不要盲目开高分辨率阴影。 因为阴影图越大,显存和带宽压力越高。

第六优先级:使用假阴影

很多移动端游戏会给角色脚下放一个半透明圆形阴影。

它不是真实阴影,但成本非常低。

适合:

  • 大量怪物
  • 低端机
  • 远处角色
  • Q 版角色
  • 俯视角游戏
  • 性能压力大的战斗场景

假阴影常见做法:

  • 一个半透明椭圆贴图。
  • 跟随角色脚底。
  • 根据地面高度调整位置。
  • 根据角色跳跃高度改变透明度和大小。

面试高分回答

NOTE

移动端阴影优化首先要控制实时阴影的数量,因为实时阴影会带来额外的 ShadowCaster Pass、DrawCall、显存和采样成本。项目里一般会让静态场景使用 Lightmap 烘焙,动态主角保留有限实时阴影,远处单位和大量小怪使用假阴影或关闭阴影。同时缩短 Shadow Distance、降低 Shadow Resolution、减少 Cascades、限制 Cast Shadows 对象,并针对不同机型做画质分档。

一句话记忆:

移动端阴影优化的核心是:静态烘焙,动态精简,远处降级,主角优先。

文章评价

读完这篇,留下你的看法

暂无审核通过的评价。

登录账号后才能评价。

本站访客数0总站访问量0本页访问量0