🎬 开场:一个"点几盏灯就崩"的惨案
小王想让场景更真实,兴冲冲地摆了 20 盏点光源、开了实时阴影,
还给每盏灯都开了阴影……结果——帧率直接腰斩,手机秒烫!🔥“灯不就是照亮吗?阴影不就是黑一块吗?怎么这么贵?”
老鸟看了直摇头:“光照和阴影是游戏里最烧性能的大户!
一盏实时光可能让物体多渲染一遍,
一个实时阴影要先渲染一张深度图再采样……
今天教你怎么’用最少的钱,办最亮的事’!”
🤔 第一幕:先搞懂为什么光照阴影这么贵
光照贵在哪
实时光照的开销: 每盏实时光 → 每个受影响像素都要算光照 ↓ 多光源叠加 → 每个像素算多次光照 ↓ 某些渲染方式下,每盏额外光 可能让物体"多渲染一遍"(额外DrawCall)! ↓ 灯越多 = 计算爆炸阴影贵在哪
实时阴影的开销(两步都贵): 第1步: 渲染阴影贴图(ShadowMap) 从光源角度渲染整个场景的深度 → 额外一遍场景渲染! (贵) 第2步: 采样阴影 每个像素采样ShadowMap判断是否在阴影里 → 额外采样 + 计算(贵) ↓ 还涉及RT切换(渲染ShadowMap到RT)!生动比喻:请人照明和画影子
光照 = 请照明师傅 每盏灯请一个师傅算怎么照 灯多 = 师傅多 = 工钱爆炸 阴影 = 请画师画影子 先从太阳角度拍张照(渲染ShadowMap) 再对着照片画每个物体的影子(采样) 两道工序都费钱! ↓ 所以光照阴影是"高消费项目" 必须精打细算!💡 第二幕:光照优化核心策略
策略1:能烘焙就别实时⭐最重要
光照分三种: 实时光照(Realtime): 每帧实时计算,最贵,但能动态变化 烘焙光照(Baked): 离线预计算好,存进光照贴图(Lightmap) 运行时直接采样,几乎免费! 但不能变化(静态) 混合光照(Mixed): 主光实时(能动),间接光烘焙 ↓ 核心原则: 静态场景光照 → 全烘焙!生动理解烘焙
烘焙就像"提前把光影画进贴图": 实时: 每帧现算光影(累) 烘焙: 美术提前算好,画进贴图 运行时: 直接贴上去(白嫖!) ↓ 静态物体(墙、地面、建筑)的光影 根本不变 → 烘焙进去,零实时开销! ↓ 这是移动端光照优化的第一大招!策略2:控制实时光源数量
✅ 实时光源越少越好 - 一个主方向光(太阳)通常够了 - 点光源、聚光灯严格控制数量 - 用"重要度"排序,只保留关键光 ↓ 移动端: 尽量1个实时方向光 + 烘焙策略3:选对光照渲染路径
前向渲染(Forward): 每盏光可能让物体多渲染一遍 → 光多时爆炸 → 但移动端常用(简单、省带宽) 延迟渲染(Deferred): 光照数量影响小(先存GBuffer再统一光照) → 适合大量光源 → 但带宽消耗大,移动端慎用! ↓ 移动端: 多用Forward + 严格控制光源数策略4:光源范围与剔除
✅ 点光源/聚光灯设置合理范围 - 范围越小,影响的物体越少 - 引擎会剔除范围外的物体 ↓ 别用超大范围的点光源(影响一大片,贵)策略5:逐顶点 vs 逐像素光照
重要的光 → 逐像素(精细,贵) 次要的光 → 逐顶点(粗糙,便宜) 更次要 → 球谐(SH,最便宜) ↓ Unity可设置每个光的渲染模式 不是所有光都需要逐像素!🌑 第三幕:阴影优化核心策略
策略1:能烘焙阴影就烘焙⭐
静态物体的阴影 → 烘焙进Lightmap! ↓ 不需要实时ShadowMap 零实时开销 ↓ 只有"动态物体"才需要实时阴影策略2:控制阴影距离(Shadow Distance)⭐关键
✅ 阴影距离越小,开销越小 远处的阴影看不清,没必要渲染 ↓ 把Shadow Distance调到"够用就行" ↓ 移动端这是最有效的阴影优化之一! ShadowMap覆盖范围小 → 精度高 + 开销小生动理解阴影距离
阴影距离像"画影子的范围": 范围大: 远处小物体的影子都画 → 累,还看不清 范围小: 只画近处能看清的影子 → 省,近处还更清晰! ↓ 远处用其他方式(如烘焙、假阴影)策略3:降低 ShadowMap 分辨率
ShadowMap分辨率越高 → 阴影越清晰但越贵 移动端: 适当降低分辨率 配合小Shadow Distance (范围小了,低分辨率也够清晰) ↓ 平衡清晰度和开销策略4:级联阴影(CSM)合理设置
级联阴影(Cascaded Shadow Maps): 近处用高精度,远处用低精度 分几级(cascade) ↓ 移动端: 减少级联数量(如1-2级) 级联越多越贵策略5:用假阴影代替实时阴影⭐移动端神器
✅ 便宜的假阴影方案: 1. Blob Shadow(投影贴图): 角色脚下贴一个圆形黑影贴图 几乎零成本! 移动端超常用 2. 预计算/烘焙阴影 3. 平面阴影(Planar Shadow): 把模型压扁投影到地面 ↓ 不是所有阴影都要"真实时阴影"!生动理解假阴影
Blob Shadow就像"脚下贴个黑圆": 真阴影: 精确计算,贵 假阴影(Blob): 脚下贴张黑色圆形贴图 → 便宜到几乎免费 → 远看效果够用! ↓ 手游里大量角色阴影都是Blob 省下海量性能!📊 第四幕:光照阴影方案对比
光照方案对比
方案 开销 能否动态 适用 ──────────────────────────────────────────── 烘焙光照 极低 否 静态场景⭐ 混合光照 中 部分 动态物体+静态环境 实时光照 高 是 必须动态变化时 逐顶点光照 低 是 次要光源 球谐(SH) 极低 是 环境光/次要阴影方案对比
方案 开销 质量 适用 ──────────────────────────────────────────── 烘焙阴影 极低 好 静态物体⭐ Blob假阴影 极低 一般 角色/移动端⭐ 实时ShadowMap 高 好 重要动态物体 平面阴影 低 一般 地面上的物体移动端推荐组合
✅ 移动端典型省电方案: 静态场景 → 烘焙光照 + 烘焙阴影 主角色 → 1个实时方向光 + Blob假阴影 或 → 小Shadow Distance的实时阴影 环境光 → 球谐/环境贴图 ↓ 实时光源尽量只留1个方向光!🛠️ 第五幕:实战设置要点
Unity 光照设置要点
✅ 静态物体标记为Static → 才能参与烘焙 ✅ 光源模式设置: Baked(烘焙) / Mixed(混合) / Realtime(实时) 静态场景光 → Baked 主光(需要动态阴影) → Mixed ✅ Lightmap设置: 合理的分辨率(别过高,占内存和包体)Unity 阴影设置要点
✅ Quality Settings / URP Asset里: - Shadow Distance: 调到够用就行(重要!) - Shadow Resolution: 移动端适当降低 - Cascade Count: 移动端1-2级 ✅ 每个光源: - 只给必要的光开阴影 - 其他光关闭阴影(Shadow Type = No Shadows)光照优化代码示例(简化概念)
移动端Shader光照简化思路: // ❌ 每个像素算复杂PBR多光源 // (移动端太贵) // ✅ 简化: // - 主光逐像素(简化光照模型) // - 环境光用球谐(便宜) // - 阴影用小范围ShadowMap或Blob half3 lighting = mainLight * NdotL // 主光(简化) + ambientSH // 环境(球谐,便宜) + bakedLightmap; // 烘焙(白嫖)⚠️ 第六幕:常见误区
误区1:滥用实时光源
❌ "多摆几盏灯更好看" → 每盏实时光都是开销 → 移动端几盏就崩 ↓ ✅ 精简实时光,善用烘焙误区2:所有物体都开实时阴影
❌ 静态物体也开实时阴影 → 浪费! 静态阴影该烘焙 ↓ ✅ 只有动态物体才需实时阴影 静态的烘焙掉误区3:Shadow Distance 设太大
❌ 阴影距离拉满 → ShadowMap覆盖巨大范围 → 精度低 + 开销大 ↓ ✅ 调到"看得清的范围"即可误区4:移动端硬上延迟渲染
❌ 移动端用延迟渲染追求多光源 → GBuffer带宽消耗巨大 → 移动端带宽扛不住! ↓ ✅ 移动端优先Forward + 控制光源数误区5:不用假阴影
❌ 角色一定要"真实时阴影" → 大量角色时开销爆炸 ↓ ✅ 远处/次要角色用Blob假阴影 省下海量性能✅ 光照阴影优化检查清单
光照优化: □ 静态场景光照烘焙了吗?⭐ □ 实时光源数量精简了吗(尽量1主光)? □ 静态物体标记Static了吗? □ 次要光用逐顶点/球谐了吗? □ 点光源范围合理吗(别过大)? □ 移动端用Forward了吗(慎用Deferred)? 阴影优化: □ 静态阴影烘焙了吗?⭐ □ Shadow Distance调到够用了吗?⭐ □ ShadowMap分辨率合理吗? □ 级联数量精简了吗(移动1-2级)? □ 只给必要的光开阴影了吗? □ 考虑用Blob假阴影了吗?⭐ 综合: □ 环境光用球谐/环境贴图了吗? □ Lightmap分辨率合理(不占过多内存)? □ 真机上测试验证了吗?🎬 一句话总结
光照与阴影优化的核心:
它们是游戏最烧性能的大户——每盏实时光都要逐像素计算,
每个实时阴影都要"先渲染 ShadowMap 再采样"(还涉及 RT 切换)。光照核心策略:能烘焙就烘焙(静态场景白嫖光影),
实时光源尽量只留一个主方向光,次要光用逐顶点/球谐。阴影核心策略:静态阴影烘焙,Shadow Distance 调到够用就行,
动态角色大胆用 Blob 假阴影——不是所有阴影都要"真实时"!
核心口诀:能烘焙就别实时,主光留一盏就够,阴影距离别拉满,静态阴影全烘焙,角色影子用Blob,移动端Forward稳!
💡 优化优先级速查表
| 优化手段 | 收益 | 难度 | 优先级 |
|---|---|---|---|
| 烘焙静态光照/阴影 | 极高 | 中 | ⭐⭐⭐⭐⭐ |
| 减少实时光源数 | 极高 | 低 | ⭐⭐⭐⭐⭐ |
| 调小 Shadow Distance | 高 | 低 | ⭐⭐⭐⭐⭐ |
| Blob 假阴影 | 高 | 低 | ⭐⭐⭐⭐ |
| 只给必要光开阴影 | 高 | 低 | ⭐⭐⭐⭐ |
| 降 ShadowMap 分辨率 | 中高 | 低 | ⭐⭐⭐⭐ |
| 减少级联数 | 中 | 低 | ⭐⭐⭐ |
| 次要光逐顶点/SH | 中 | 中 | ⭐⭐⭐ |
💡 一句话记住核心:
静态的一切都烘焙掉,动态的能省则省。
实时光源和实时阴影是"奢侈品",
只花在最重要的地方(主光、主角),
其余用烘焙和假阴影"平价替代"!
🔮 延伸:光照阴影与移动端优化哲学
【又一次印证核心痛点】 光照阴影为什么在移动端更要优化? 实时光照 → 大量像素计算(算力) 实时阴影 → 渲染ShadowMap + RT切换(带宽!) + 额外采样(带宽!) ↓ 又回到那个词: 带宽 + 算力 ↓ 烘焙 = 把"实时算力+带宽"变成"预计算+采样" = 用离线换运行时 ↓ 移动端优化哲学再次体现: "能预计算就预计算,能省带宽就省带宽!"