解释Spherical Harmonics与Ambient Probe的压缩
解读
面试官抛出此题,核心想验证三件事:
- 你是否真的在移动平台上做过光照优化,而不是只会用Lighting Settings面板“点两下”;
- 能否把数学概念翻译成工程落地的语言——“SH系数怎么存、怎么压、怎么还原”;
- 对Unity管线里内置压缩策略与自定义策略的边界是否清楚,能否在“效果-包体-性能”三角里做权衡。
答得太浅(“就是3阶球谐压缩颜色”)会被追问带宽,答得太深(“我实现了Zonal Harmonics+Clustered PCA”)又容易被质疑过度设计。国内项目普遍要跑在千元安卓机上,所以务必把“为什么压、压多少、压完怎么还原”讲成一条量化指标链。
知识点
- 球谐函数(SH):用正交基把球面信号投影成系数向量,Unity光照贴图环境光默认L2阶(9系数,RGB各9=float27)。
- Ambient Probe:引擎运行时对LightProbe/Environment采样后生成的“天空盒+GI”颜色球,移动端每帧插值,带宽敏感。
- 量化压缩:Unity内置把27×32bit→27×8bit(0-255),再打包成7个4×8bit纹理(RGB9方案),运行时GPU采样再scale回实数。
- 自定义压缩:
- Range Fit:先对场景所有Probe做Min-Max归一化,存一张4×8bit的per-probe scale/bias,系数存8bit,误差≈0.8‰;
- Octahedral+BC7:把方向光部分抽出来做Octahedral编码,漫反射部分用L1+Scale,整体塞进BC7块,iOS A13+设备带宽降42%;
- L1+Occlusion:低端机直接砍到L1(4系数),用AO通道补能量,Shader里
dot(shAr, shBr)还原,ALU增4指令,包体省60%。
- 量化误差与bandpass:8bit量化会把高频方向光压成“灰”,解决方法是** directional dominant light extraction**,把最强光抽出去单独做太阳方向SH,剩余能量再L2压缩,千元机实测ΔE<2.3。
- Unity版本差异:2021.3以前CPU端解码,2022.1+ GPU端采样后scale,带宽减半但要求GLES3.1+AEP,国内发行需做双路径回落。
答案
“我们在上一个开放世界项目里把LightProbe数据从27 floats压到8 bytes,分三步:
- 离线归一化:对全图2000+ Probe做Range Fit,每Probe存一个maxRGB,系数用8bit量化,误差控制在1‰以内;
- Shader还原:GPU端用4条ALU把8bit系数scale回实数,再乘maxRGB,带宽从108 B/probe降到8 B,低端机帧率提升2.1 ms;
- 太阳方向补偿:把最强方向光抽出来单独做1阶SH,剩余能量用L2,既保留高光方向,又避免量化把太阳压灰。
最终包体里LightProbeAsset从12 MB→1.8 MB,红米Note 10连续跑30 min无掉帧,美术肉眼无差异。”
拓展思考
如果面试官继续追问“还能再压一半吗”,可以抛出Clustered SH + BC6H方案:
- 离线K-Means把场景Probe聚64簇,每簇存一份L2基,实例只存clusterIdx+scale,GPU用uint8索引查表;
- 方向高频部分用BC6H立方图存Sun Radiance,Shader里采样后与SH低频相加,整体再省50%内存,但要求GLES3.2+Vulkan1.1,国内需做机型白名单。
落地时要给发行部一张“压缩等级-覆盖率-效果”对照表,让运营在后台动态下发,避免“一刀切”导致低端机花屏。