【笔记】Spherical Harmonic Lighting 球谐光照渲染初探

Spherical Harmonic Lighting

球谐光照渲染初探

漫反射环境光

正常情况下,漫反射计算公式为:

\[L(p,w_o) = \int_{\Omega} L(p,w_i) n\cdot w_idw_i \]

也就是在半球空间\(\Omega\)中,从观察方向\(w_o\)看去,某一点\(p\)的光照就是沿着入射光方向\(w_i\)积分,\(n\)为法线

而加上环境光的条件,可以认为光源足够远,那么模型相对来说就足够小

这样,任何一点都可以看成天空球的中心,可以将积分中的\(p\)去掉

\[L(p,w_o) = \int_{\Omega} L(w_i) n\cdot w_idw_i \]

也就是说,某一点\(p\)的光照,实际上只和它的法线有关,即

\[L(n) = \int _{\Omega} n\cdot L(w_i)\cdot w_i dw_i \]

那么如果给法线打一个表,计算的时候只需要求出这个点的法线,然后查表(采样)即可

这就是Image Based Lighting (IBL)

球谐函数的性质

这样的方法,将对球面空间的积分简化为了纹理采样,需要存储一张纹理图

继续改进就要用到球谐函数了,感觉有点类似于球里面的三角函数

直接给出两个性质

  • 正交完备性

就是说球谐函数是两两正交的,自己和自己积分是1,其他情况是0

  • 旋转不变性

环境光变化之后,只需要简单的计算就能得到新的结果

球谐函数

把一个函数展开成球谐函数

\[f(x) = \sum_{l=0}^{\infty}\sum_{m=-l}^{l}c_l^mY_l^m(x) \]

这里的\(c_l^m\)就是球谐系数,改变枚举次序,得到

\[f(x) = \sum_{i=0}^{N} c_i Y_i(x) \]

其中\(i=l*(l+1)+m\)\(N=l^2\)

可以投影(有点像某种反演)得到球谐系数

\[c_l^m = \int _{\Omega} f(w) Y_l^{w} (w) dw \]

虽然还不知道\(Y_l^m\)里到底是什么东西,不过不重要,先试着简化光照方程

原始的光照方程为:

\[L(p,w_o) = \int_{\Omega} L(p,w_i) n\cdot w_idw_i \]

定义\(light(w) = L(p,w)\)\(t(w) = nw\)

然后分别对这两个函数进行球谐展开

\[light(w) = \sum_{i} L_i Y_i(w)\\ t(w) = \sum_{i} t_i Y_i(w) \]

也就是用一组球谐系数\(\{L_i\}\)\(\{t_i\}\)来表征这两个函数,实际计算时只需要前3项就很准了

然后把这两个展开的函数代回去

\[\begin{align} L(p,w_o) &= \int_{\Omega} L(p,w_i) n\cdot w_idw_i\\ &=\int_{\Omega} light(w) t(w) dw\\ &= \int_{\Omega}\sum_{i} L_i Y_i(w) \sum_{i} t_i Y_i(w) \end{align} \]

显然可以交换求和次序,并提出来常数

\[\begin{align} L(p,w_o) &= \sum_{j} \sum_{i} L_i t_j \int_{\Omega}Y_i(w)Y_j(w) dw \end{align} \]

由于正交性,只看\(i=j\)的项就行,后面的积分为1

\[L(p,w_o) = \sum_i L_it_i \]

于是某点\(p\)的光照就变成常数的乘积了

球谐系数

然后反演求一下\(L_i\)

\[L_i = \int_{\Omega} L(p,w) Y_i(w) dw \]

前面提到,这里的\(L(p,w)\)实际上只与\(p\)的法线有关,也就算可以写成\(L(n)\)

所以\(L_i\)就是

\[L_i = \int_{\Omega}L(n_p)Y_i(w) dw \]

\(L_i\)可以直接对半球空间\(\Omega\)积分 (为什么?)

具体来说就是枚举天空盒的坐标\(p\),归一化(投影到球面)从而枚举球面坐标

for(pixel p : Cubemap){
  Li += p.color * Yi(normalize(p.pos)) * dw
}

至于\(t_i\),略困难一些

\[t_i = \int_{\Omega}n\cdot w \cdot Y_i(w) dw \]

这里必须要知道法线信息,不够优美了

球坐标系

复习一下微积分基本知识,直角坐标与球坐标的转换

\[\begin{align} x &= r \sin\theta \cos \phi \\ y &= r \sin\theta \sin \phi \\ z &= r \cos \theta \end{align} \]

旋转球谐函数

这里的旋转不是对函数旋转,而是对输入的自变量旋转

假设对一个三维空间的点\(\mathbf{u}\)做旋转\(R^{\alpha,\beta,\gamma}\),再传进去一个球谐函数\(Y_l^m\)

\[Y_l^m(R^{\alpha,\beta,\gamma}(\mathbf{u})) = \sum_{m'=-l} ^{l} D_{m,m'}^l(R^{\alpha,\beta,\gamma})Y_l^{m'}(\mathbf{u}) \]

其中

\[D_{m,m'}^{l}(R^{\alpha,\beta,\gamma}) = \exp (-im'\alpha)\exp(-im\gamma) d_{m',m}^l (\beta) \]

其中\(d_{m',m}^l\)为维格纳D矩阵,表达式很复杂,当成一个黑盒

这里是为了说明,假设对于某点\(\mathbf{u}\),如果我们已经算出来\(Y_l^m(\mathbf{u})\)

如果现在旋转一下\(\mathbf{u}\),那么不需要重新计算\(Y_l^m(\cdot)\),只需要把之前的结果乘以某个矩阵就行

简单说,就是对输入的旋转,可以被在输出里分离

那么,本来对于某个函数\(f(\mathbf{u})\)展开的形式是:

\[f(\mathbf{u}) = \sum_{l}\sum_{m=-l}^{l} c_l^m Y_l^m (\mathbf{u}) \]

旋转一下\(u\),结果就是

\[f(\mathbf{u}) = \sum_{l} \sum_{m=-l}^{l} g_l^m Y_l^m(\mathbf{u}) \]

也就是把每个系数都乘上了一个和旋转有关的值,具体来说是:

\[g_l^{m'} = \sum_{m=-l}^{l}c_l^mD_{m,m'}^l (R^{\alpha,\beta,\gamma}) \]

这样每次旋转就不需要重新算\(Y\)了,只需要对系数做一些变换即可

又见漫反射环境光

先复习一下之前的漫反射环境光公式

\[L(n) = \int_{\Omega}L(w_i) \cdot n\cdot w_idw_i \]

自变量直接写成了\(n\),代表所求一点\(p\)的法线,可以理解为这点接受到天空的所有光之和

现在重新建一个坐标系,以法线\(n\)为z轴,称为局部坐标系,这里面所有变量都带撇

\[L(n) = \int_{\Omega} L(w_i')t(\theta')dw_i' \]

其中\(t(\theta') = n\cdot w_i' = \cos \theta'\)

现在将入射光转换到局部坐标系里(只需要旋转)

\[w_i = R^{\alpha,\beta,\gamma} w_i' \]

这里的矩阵\(R^{\alpha,\beta,\gamma}\)实际上就是世界坐标系里法线\(n\)转到z轴的角度,类似于把球转了一下

posted @ 2023-02-15 16:00  GhostCai  阅读(274)  评论(0编辑  收藏  举报