球谐光照(Spherical Harmonics Lighting,简称SH光照)是Unity中一种高效编码低频环境光照 的技术,主要用于Light Probe(光照探针) 和Skybox环境光 的漫反射计算。它的核心价值在于:用极少的参数(通常9个浮点数)就能近似表达一个物体周围360°的入射光照分布,并在Shader中快速计算出物体表面接收到的漫反射光。
下面从"为什么需要"、"数学原理"、"Unity中的具体应用"三个层面来解释。
一、为什么需要球谐光照?
在实时渲染中,如果要精确计算物体表面某点接收到的间接漫反射光(比如蓝色墙壁反射到红色地毯上的光),理论上需要对该点上半球所有方向的入射光进行积分,这在运行时是不可能完成的。
传统做法:
-
预计算辐照度贴图:对空间每个点存储一个cubemap → 内存爆炸。
-
实时Ray Tracing:性能无法满足游戏需求。
球谐光照提供了一个折衷方案:
-
离线预计算:将场景中每个Light Probe位置周围的入射光照,投影到球谐基函数上,存储少量系数(例如27个float,RGB各9个)。
-
运行时还原:Shader中只需用表面法线点乘这些系数,就能快速得到该点接收到的漫反射光强度。
这样就实现了"用较低的存储和计算成本,获得非常平滑、无方向细节但整体正确的间接漫反射效果"。
二、球谐光照的数学原理(通俗版)
1. 球谐函数------球面上的傅里叶级数
傅里叶级数可以用正弦余弦函数的叠加来逼近任意周期性波形。类似地,球谐函数是定义在球面上的一组正交基函数,可以用它们的加权和来逼近任意定义在球面上的函数(比如从某个点看向各个方向的光照强度)。
球谐函数有很多阶(Order):
-
0阶:1个基函数(常数项),代表平均亮度。
-
1阶:3个基函数(线性项),能表示方向性(如上下比左右亮)。
-
2阶:5个基函数(二次项),能表示更复杂的分布(例如两极性)。
-
3阶:7个基函数......阶数越高,能重现的细节越丰富。
实际应用中,渲染漫反射(低频)只需前3阶(共1+3+5=9个系数),因为漫反射光照相当于对原始光照进行了低通滤波,高频细节被抹除。
2. 渲染方程中的漫反射简化
在一点p,漫反射颜色公式为:

其中 Li(p,ωi)Li(p,ωi) 是该点从方向 ωiωi 入射的光照。
如果我们将 Li(p,ωi)Li(p,ωi) 投影到球谐基函数上,得到系数 cl,mcl,m,同时将漫反射的传输函数 (即 max(0,n⋅ωi)max(0,n⋅ωi))也投影到同一组基上,那么积分结果就等于两组系数的点积。最终化简为:

其中 klkl 是预先算好的常数(与球谐阶数相关),Yl,m(n)Yl,m(n) 是球谐基函数在法线方向n上的取值。
在Shader中,这部分计算被合并为一个法线方向的九次多项式:
// Unity内置的球谐光照计算(简化逻辑)
half3 SHLighting (half3 normal) {
half4 SHAr = unity_SHAr;
half4 SHAg = unity_SHAg;
half4 SHAb = unity_SHAb;
half4 SHBr = unity_SHBr;
half4 SHBg = unity_SHBg;
half4 SHBb = unity_SHBb;
half4 SHC = unity_SHC;
half3 res = SHEvalLinearL0L1(normal, SHAr, SHAg, SHAb);
res += SHEvalLinearL2(normal, SHBr, SHBg, SHBb, SHC);
return res;
}
三、Unity中球谐光照的具体应用
1. 主要载体:Light Probe
-
Light Probe Group 组件可以在场景中放置多个光照探针。
-
Unity在烘焙时,为每个探针位置计算该点的入射光照立方贴图 ,然后投影到3阶球谐函数 ,得到RGB三组9个系数(共27个float),存储在光照数据中。
-
运行时,物体使用距离最近的几个探针,通过插值获取当前点的球谐系数。
2. Shader中的使用方式
对于受Light Probe影响的物体(通常是静态或动态物体),Unity会自动将插值后的球谐系数通过 unity_SHAr、unity_SHAg 等内置变量传递给Shader。
获取球谐漫反射光照的标准代码:
// 在世界空间法线方向计算球谐光照
half3 ambient = ShadeSH9(float4(worldNormal, 1.0));
或者手动调用内置函数:
half3 indirectDiffuse = SampleSH(worldNormal);
3. 与传统环境光的区别
| 特性 | 传统 Cubemap 环境光 | 球谐光照 (SH) |
|---|---|---|
| 存储量 | 高(6张纹理) | 极低(27个float) |
| 精度 | 高(可表现细节) | 低(只有低频信息) |
| 计算开销 | 高(需要采样立方体贴图) | 极低(几个点积和多项式) |
| 适用场景 | 静态背景反射、高光环境 | 动态物体间接漫反射、移动端全局光照 |
四、局限性
-
只能表示低频光照
球谐无法表现尖锐的光影变化,例如点光源直接照明、阴影边缘。因此它只适合做间接光/环境光。
-
无法用于高光反射
镜面反射需要高频的环境信息(如清晰的窗户倒影),球谐阶数不够。镜面反射仍需使用反射探针(Reflection Probe)的Cubemap。
-
仅适用于漫反射
球谐光照的传输函数是低通的,与漫反射BRDF匹配,不适合光泽或镜面材质。
总结一句话
球谐光照是一种将周围环境光照"压缩"成9个系数的数学技巧,在Shader中通过法线方向快速恢复出物体表面接收到的平滑漫反射光,是Unity实现Light Probe和轻量级全局光照的核心技术。