# 谐函数
谐函数就是满足 拉普拉斯方程
的函数,这个方程的数学意义是梯度的散度为 0,物理意义是稳定,集合形状上像椭圆
∇2u=0
- 一个不与外界做能量交换的系统,它的总能量是稳定的,尽管他内部可能进行着能量交换
- 一个不受外力的系统,它的整体加速度是 0,尽管内部的物体可能在运动
- 一个震动的理想弹簧、一个没有组里的理想钟摆,他们将一直规律运动下去
这些都是稳定状态
三维直角坐标系下的 拉普拉斯方程
:
∇2u=∂x2∂2u+∂y2∂2u+∂z2∂2u=0
# 球坐标下的拉普拉斯方程
将球坐标转换成的三维直角坐标系参数方程代入
⎩⎪⎨⎪⎧x=rsinθcosϕy=rsinθsinϕz=rcosθ
得到球坐标下的拉普拉斯方程如下:
∇2u=r21r∂(r2∂r∂u)+r2sinθ1∂θ∂(sinθ∂θ∂u)+r2sin2θ1∂θ∂∂ϕ2∂2u=0
# 球谐函数
对于任意一个球坐标系下的函数:u(r,θ,ϕ),分离变量:
u(r,θ,ϕ)=R(r)Y(θ,ϕ)
- R® 表示距离部分
- Y (\theta,\phi) 表示方向部分,即球谐函数
# 球谐函数表达式的推导
将 R 和 Y 代入拉普拉斯方程
r2Ydrd(r2drdR)+r2sinθR∂θ∂(sinθ∂θ∂Y)+r2sin2θR∂ϕ2∂2Y=0
移项化简:
R1drd(r2drdR)=−sinθY1∂θ∂(sinθ∂θ∂Y)−Y1sin2θ1∂ϕ2∂2Y
假设两边函数同时等于一个常数,设为 l (l+1),得到两个新方程:
drd(r2drdR)−l(l+1)R=0sinθ1∂θ∂(sinθ∂θ∂Y)+sin2θ1∂ϕ2∂2Y+l(l+1)Y=0
其中第二个方程被称为 球函数方程
进一步分离变量,令Y(θ,ϕ)=Θ(θ)Φ(ϕ)
可以得到两个常微分方程,只记第二个
sinθdθd(sinθdθdΘ)+[l(l+1)sin2θ−λ]Θ=0
经过一系列转换可以得到 l 阶连带勒让德方程
(1−x2)dx2d2Θ−2xdxdΘ+[l(l+1)−1−x2m2]Θ=0
上面方程的解,Θ 即为连带勒让德函数,记为Plm(x)
Plm(x)=Θ=(1−x2)2mPl[m](x)
- Pl[m](x) 是Pl(x) 的 m 阶导数
- Pl(x) 是勒让德函数,它的前几阶为
0 1
1 x
2 21(3x2−1)
3 21(5x3−3x)
Y 对 l 的 m 阶导数表达式为:
Ylm(θ,ϕ)=(−1)m4π2l+1(1+∣m∣)!(l−∣m∣)!Plm(cosθ)eimϕ
同理结合 P 可以求出 Y 的前两阶表达式,然后可以得到其直角坐标系的表达式,下面链接可以看到
而 Unity 或者通常游戏开发中是直接将一二阶的球谐函数近似值的实际公式求出,然后直接将参数代入求出的
比如下列:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| SH9 CalculateCoefficients(float3 normal) { float A0 = Pi; float A1 = (2.0f * Pi) / 3.0f; float A2 = Pi * 0.25f;
SH9 sh; sh.c[0] = 0.282095 * A0; sh.c[1] = 0.488603 * normal.x * A1; sh.c[2] = 0.488603 * normal.z * A1; sh.c[3] = 0.488603 * normal.y * A1; sh.c[4] = 1.092548 * normal.x * normal.z * A2; sh.c[5] = 1.092548 * normal.y * normal.z * A2; sh.c[6] = 1.092548 * normal.y * normal.x * A2; sh.c[7] = (0.946176 * normal.z * normal.z - 0.315392) * A2; sh.c[8] = 0.546274 * (normal.x * normal.x - normal.y * normal.y) * A2;
return sh; }
float3 CalculateColor(SH9 coeffs) { float3 color = coeffs.c[0] * _SHData[0].rgb
+ coeffs.c[1] * _SHData[1].rgb + coeffs.c[2] * _SHData[2].rgb + coeffs.c[3] * _SHData[3].rgb
+ coeffs.c[4] * _SHData[4].rgb + coeffs.c[5] * _SHData[5].rgb + coeffs.c[6] * _SHData[6].rgb + coeffs.c[7] * _SHData[7].rgb + coeffs.c[8] * _SHData[8].rgb; return color; }
|