如何使用LPPV在大型开放世界中补光

解读

国内一线厂面试问“LPPV”并不是想听概念背诵,而是考察候选人能否在开放世界海量动态光源移动端带宽/发热之间做权衡。LPPV(Light Probe Proxy Volume)本质是把“光照探针”从“点”扩展成“体”,让GPU Instance/植被/飘带这类无法烘焙的物体也能拿到局部光照信号,同时避免实时光。答题时要围绕“为什么用→怎么用→怎么省→怎么防错”四步展开,并给出Unity版本差异(2021 LTS之后支持Streaming,2019 LTS需要手动Slice),否则会被追问“你项目到底跑过没”。

知识点

  1. LPPV数据格式:3D纹理,每个体素存SH L2系数(RGB各9维),带宽≈144 byte/voxel。
  2. Proxy Volume划分策略:**世界分块(World Subdivision)对象跟随(Object Anchor)**两种模式;开放世界必须选World,否则边界探针无法跨块复用。
  3. 更新频率TimeSlicing分帧刷新,每帧最多更新MaxAtlasCells(默认64)个体素,防止卡顿。
  4. 内存预算公式
    Memory(MB) = (WorldSize.x/CellSize) × (WorldSize.y/CellSize) × (WorldSize.z/CellSize) × 144 / 1024 / 1024
    移动端建议≤8 MB,对应256×16×256世界、CellSize 4 m。
  5. Shader采样UnitySampleBakedProbe(float3 worldPos, float3 normal, LPPV lodVolume),注意worldPos需减去VolumeOrigin,否则采样错位。
  6. 兼容性与坑
    • SRP Batching会打断LPPV常量缓冲,需要在Shader里手动UNITY_INSTANCING_BUFFER_START(Probes)
    • WebGL1不支持3D纹理,需回退到2D Atlas,性能掉30%。
    • Vulkan+Adreno 5xx驱动对3D纹理更新有bug,必须开Force OpenGL ES3

答案

“我们在25 km²的开放世界里用LPPV解决昼夜循环下植被与岩石的局部补光问题,核心分四步:

  1. 离线预烘焙:把世界按256×4×256 m切块,每块内用GPU Progressive Lightmapper烘焙一套L1 Probe,再离线升阶到SH L2,存进Streaming AssetBundle;运行时按需异步加载,内存峰值压到7.2 MB
  2. 运行时更新:只更新玩家周围3×3块,用Job System+Burst每帧更新最多64个体素,耗时<1.2 ms(小米10测试)。
  3. 着色器端:植被Shader里用UnitySampleBakedProbe取LPPV,再与主方向光做NdotL混合,强度用Per-InstanceAO Fade控制,避免过亮。
  4. 边界处理:块与块之间用Trilinear+Normal Bias 0.05做混合,玩家跨块时Volume Origin0.5 s插值,肉眼无跳变。
    最终同屏5 k颗SpeedTreeAdreno 650上GPU成本仅0.8 ms,相比全实时光节省4.3 ms,发热降3 ℃,包体只增加2.1 MB。”

拓展思考

如果面试官继续追问“LPPV与Screen Space GI怎么选”,可以答:
“LPPV适合低频、大范围的物体,SSGI适合高频、相机周边;我们实际混合使用:玩家半径50 m内用SSGI(Unity 2022 SSAO+Screen Space Ray Trace)补高频AO50 m外靠LPPV;两者在50 m处用Depth Fade混合,既省带宽又保细节。
若项目要跑Quest2这类Tile Memory极小的设备,可把LPPV体素降到SH L1,内存减半,再用ALU在Shader里把L1还原成L2,误差肉眼不可见,GPU耗时再降0.3 ms。”