如何动态调整Shadow Distance以节省DrawCall

解读

在国内 Unity 项目面试中,这道题表面问“阴影距离”,实质考察候选人对 阴影级联、Shadow Map 分辨率、渲染队列拆分、GPU Instancing 失效条件 的综合理解。
很多候选人直接回答“改 QualitySettings.shadowDistance”,却忽略了 Shadow Distance 与 DrawCall 之间并非线性关系

  1. 当 Shadow Distance 缩小,阴影投射物数量减少,CPU 端提交 DrawCall 确实下降;
  2. 但 Unity 的阴影管线会 按级联(Cascade)拆分渲染,如果级联分割策略不变,可能只减少最后一级级联的物体,DrawCall 降幅有限;
  3. 若项目开启了 GPU Instancing,阴影 Pass 一旦因为级联或阴影距离变化导致常量缓冲区不同,会 强制打断合批,反而让 DrawCall 反弹。
    因此,面试官想听到的是:用运行时数据驱动 Shadow Distance,在画质与批次数之间做闭环权衡,而不是简单调一个字段。

知识点

  1. 阴影管线开销公式
    总阴影 DrawCall ≈ 投射物数量 × 级联数 × 1(无 Instancing)或 1/instancing 倍数。
  2. QualitySettings.shadowDistance 与 Shadow.mask 的交互
    当使用 Shadowmask 模式,shadowDistance 之外的光源会退回到烘焙阴影,CPU 不再实时绘制,DrawCall 直接归零。
  3. 级联分割算法
    Unity 默认三层级联,比例 0.067/0.2/1.0。缩小 shadowDistance 时,最后一级占比最大,收益最高;但前三层如果仍覆盖大量物体,收益递减。
  4. GPU Instancing 失效场景
    阴影 Pass 中,unity_LightShadowBias、unity_WorldToShadow[] 矩阵 随级联变化,若常量缓冲区不同,Instancing 会被打断。
  5. 移动端硬件特性
    国内主流中低端机(Adreno 610、Mali-G52)在 shadowDistance > 80 m 时,Shadow Map 分辨率被迫降到 512,否则带宽爆炸;因此 动态上限必须参考 GPU 型号与带宽预算

答案

  1. 采集运行时指标
    Camera.RenderLoop 回调中,每帧统计 Shadow DrawCall 数量UnityStats.shadowDrawCallsFrameDebugger 接口),同时记录 主相机可见物体数量GPU 帧时间
  2. 建立反馈函数
    设定目标 DrawCall 阈值,例如 120。当连续 0.5 s 超过阈值,且 GPU 时间 > 预算(如 16 ms),触发 降级策略
    float ReduceShadowDistance()
    {
        float current = QualitySettings.shadowDistance;
        float step = Mathf.Max(5f, current * 0.15f);   // 步长随距离自适应
        float next = Mathf.Max(30f, current - step);   // 保底 30 m
        return next;
    }
    
  3. 避免 Instancing 打断
    OnPreRender 中统一设置 全局阴影 bias级联分割比例,确保同一帧内所有阴影 Pass 使用相同常量缓冲区;同时把 阴影投射物材质开启 Enable GPU Instancing,并在 Shader 里使用 multi_compile_instancingUNITY_SHADOW_COORDS 宏。
  4. 分级回退策略
    若 shadowDistance 已降到 30 m 仍不达标,则 降级阴影级联数QualitySettings.shadowCascades = 2),再不行就 退到 1 级联 + 16 bit Shadow Map;最终兜底方案是 切换 Shadowmask 模式,完全关闭实时阴影。
  5. 热更新兼容
    把上述逻辑封装到 ShadowLODManager 中,配置表通过 Addressables 热更,确保线上可按机型快速调参,无需重新打包安装包。

拓展思考

  1. CSM 与 Tile-Based Shadow 结合
    未来项目可引入 Unity 2022.3 的 Adaptive Probe Volume + Tile-Based Shadow,把 shadowDistance 动态缩小到 50 m 以内,远距离用 Probe 缓存,彻底消灭远距离阴影 DrawCall。
  2. GPU-Driven 渲染管线
    在 URP 14.x 中,使用 GPU Culling + DrawIndirect 把阴影投射物也走 GPU Driven,CPU 端 DrawCall 恒定为 1,shadowDistance 调整只影响 GPU 工作量,不再受 Instancing 打断困扰。
  3. 网络同步场景
    对于 大世界多人游戏,shadowDistance 需与 Interest Management 联动:玩家周围 30 m 高保真阴影,30-80 m 投射低分辨率阴影,80 m 外直接关闭,既省 DrawCall 又省流量,符合国内 4G/5G 波动环境。