double kml =sqrt((2.0* l +1)*Factorial(l -abs(m))/(4.0* M_PI *Factorial(l +abs(m))));
求解
P
l
m
(
c
o
s
θ
)
P_l^m(cos\theta)
Plm(cosθ),根据球谐函数如下性质,求得
c
o
s
θ
cos\theta
cosθ 在任意基函数的值
注: -. 当
P
m
m
(
x
)
P_m^m(x)
Pmm(x) 中
m
=
0
m=0
m=0时,值为1,不是0!(这里公式错了) -. 因为基函数可以不需要求解
(
−
1
)
m
(-1)^m
(−1)m (系数的正负可以替代),所以在一些表达式中无正负号。
doubleEvalLegendrePolynomial(int l,int m,double x){// Compute Pmm(x) = (-1)^m(2m - 1)!!(1 - x^2)^(m/2), where !! is the double factorial.double pmm =1.0;// P00 is defined as 1.0, do don't evaluate Pmm unless we know m > 0if(m >0){double sign =(m %2==0?1:-1);
pmm = sign *DoubleFactorial(2* m -1)*pow(1- x * x, m /2.0);}if(l == m){// Pml is the same as Pmm so there's no lifting to higher bands neededreturn pmm;}// Compute Pmm+1(x) = x(2m + 1)Pmm(x)double pmm1 = x *(2* m +1)* pmm;if(l == m +1){// Pml is the same as Pmm+1 so we are done as wellreturn pmm1;}// Use the last two computed bands to lift up to the next band until l is// reached, using the recurrence relationship:// Pml(x) = (x(2l - 1)Pml-1 - (l + m - 1)Pml-2) / (l - m)for(int n = m +2; n <= l; n++){double pmn =(x *(2* n -1)* pmm1 -(n + m -1)* pmm)/(n - m);
pmm = pmm1;
pmm1 = pmn;}// Pmm1 at the end of the above loop is equal to Pmlreturn pmm1;}
当
m
<
0
m<0
m<0时,根据对称性质
即:偶数取反,奇数不变。(因为基函数可以不考虑符号正负,故可以认为如下等式成立)
P
l
−
m
=
P
l
m
P_l^{-m} = P_l^m
Pl−m=Plm
求解
e
i
m
ϕ
=
c
o
s
(
m
ϕ
)
+
i
⋅
s
i
n
(
m
ϕ
)
e^{im\phi} = cos(m\phi) + i·sin(m\phi)
eimϕ=cos(mϕ)+i⋅sin(mϕ)。【为什么乘以
2
\sqrt 2
2,网上没有找到证明,但是确实乘以
2
\sqrt 2
2才能得到正确结果!】
当m>0时,
e
i
m
ϕ
=
2
∗
c
o
s
(
m
ϕ
)
e^{im\phi} = \sqrt{2} * cos(m\phi)
eimϕ=2∗cos(mϕ)
当m<0时,
e
i
m
ϕ
=
2
∗
s
i
n
(
−
m
ϕ
)
e^{im\phi} = \sqrt{2} * sin(-m\phi)
eimϕ=2∗sin(−mϕ)
S
H
系
数
(
l
,
m
)
=
∑
i
s
a
m
p
l
e
N
u
m
L
e
(
i
)
∗
S
H
基函
数
(
l
,
m
)
(
D
i
r
(
i
)
)
∗
d
A
SH系数_{(l,m)} = \sum_i^{sampleNum} Le(i) * SH基函数_{(l,m)}(Dir(i)) * dA
SH系数(l,m)=i∑sampleNumLe(i)∗SH基函数(l,m)(Dir(i))∗dA 其中:
∑
i
s
a
m
p
l
e
N
u
m
=
∑
天空盒
k
6
∑
y
h
e
i
g
h
t
∑
x
w
e
i
g
h
t
\sum_i^{sampleNum} = \sum_{天空盒k}^6 \sum_y^{height} \sum_x^{weight}
∑isampleNum=∑天空盒k6∑yheight∑xweight
// 计算球谐系数float sumWeight =0;for(int i =0; i <6; i++){for(int y =0; y < height; y++){for(int x =0; x < width; x++){// TODO: 此处你需要计算每个像素下cubemap某个面的球谐系数// 方向
Eigen::Vector3f dir = cubemapDirs[i * width * height + y * width + x];// 入射光int index =(y * width + x)* channel;
Eigen::Array3f Le(images[i][index +0], images[i][index +1],
images[i][index +2]);// 立体角float dA =CalcArea(x, y, width, height);// 计算球谐系数for(int l =0; l <= SHOrder; l++){for(int m =-l; m <= l; m++){// Eigen库不能在Vector3d,Vector3f之间相互赋值double basic_fun_value = sh::EvalSHSlow(l, m,Eigen::Vector3d(dir.x(), dir.y(), dir.z()).normalized());
SHCoeffiecents[sh::GetIndex(l, m)]+= Le * basic_fun_value * dA;}}}}}return SHCoeffiecents;
2. 预计算传输项球谐函数
∫
Ω
V
(
i
)
B
R
D
F
(
i
,
o
)
m
a
x
(
n
⋅
w
i
,
0
)
d
i
\int_{\Omega}\quad V(i)\quad BRDF(i,o) \quad max(n\cdot w_i,0)\quad di
∫ΩV(i)BRDF(i,o)max(n⋅wi,0)di 注意:若材质为Diffuse,则
B
R
D
F
(
i
,
0
)
=
1
/
π
BRDF(i,0)=1/\pi
BRDF(i,0)=1/π;
将函数展开到球谐函数上,函数有:
无阴影函数
有阴影函数
有阴影且多次反射函数
这些函数都是离散的,所以需要采样来得到。
无阴影函数
输入:顶点法线
n
n
n、采样方向
w
i
w_i
wi 输出:
1
π
m
a
x
(
n
⋅
w
i
,
0
)
\frac{1}{\pi} max(n\cdot w_i,0)
π1max(n⋅wi,0)
有阴影函数
输入:顶点位置
v
v
v,顶点法线
n
n
n、采样方向
w
i
w_i
wi 处理:
V
i
s
i
b
i
l
i
t
y
=
在
v
点向
w
i
方向发射射线,检测是否碰到物体
Visibility =在v点向w_i方向发射射线,检测是否碰到物体
Visibility=在v点向wi方向发射射线,检测是否碰到物体 输出:
1
π
m
a
x
(
n
⋅
w
i
,
0
)
∗
V
i
s
i
b
i
l
i
t
y
\frac{1}{\pi} max(n\cdot w_i,0) * Visibility
π1max(n⋅wi,0)∗Visibility
有阴影且多次反射函数
需要先进行有阴影函数处理,得到每个顶点的SH系数。 对于每个顶点,递归调用函数computeInterreflectionSH(&m_TransportSHCoeffs, v, n, scene, 1)。