基于物理的渲染(PBR)
f
s
=
D
∗
G
∗
F
4
∗
(
n
⃗
⋅
l
⃗
)
∗
(
n
⃗
⋅
v
⃗
)
f_{s}=\frac{D * G * F}{4 *(\vec{n} \cdot \vec{l}) *(\vec{n} \cdot \vec{v})}
fs=4∗(n⋅l)∗(n⋅v)D∗G∗F
D 法线分布函数:某个点上,法线指向指定方向上的概率。
G 几何函数:光线由于遮蔽、阴影,所以部分光线被阻挡;G即为光线没有被阻挡的概率。
F Fresnel方程:表示一束光线打在某个方向的平面上的反射的能量比率。
4
∗
(
n
⃗
⋅
l
⃗
)
∗
(
n
⃗
⋅
v
⃗
)
{4 *(\vec{n} \cdot \vec{l}) *(\vec{n} \cdot \vec{v})}
4∗(n⋅l)∗(n⋅v)为矫正量
微表面模型需要用到的参数:
V
V
V:光线的反射方向(单位向量)
I
I
I:光线的入射方向的反方向(单位向量)
m
=
h
m=h
m=h:
V
V
V和
I
I
I的中间向量(单位向量)
N
N
N :为宏观平面法线(单位向量)
α
\alpha
α:为粗糙度
∈
[
0
,
1
]
\in[0,1]
∈[0,1]。
法线分布函数 D
微平面的法线分布函数D(m)描述了微观表面上的表面法线m的统计分布。
业界较为主流的法线分布函数是GGX(Trowbridge-Reitz),因为具有更好的高光长尾。
D
(
n
,
h
,
α
)
=
α
2
π
(
(
n
⋅
h
)
2
(
α
2
−
1
)
+
1
)
2
D(n, h, \alpha) = \frac{\alpha^{2}}{\pi \left((n \cdot h)^{2}\left(\alpha^{2}-1\right)+1\right)^{2}}
D(n,h,α)=π((n⋅h)2(α2−1)+1)2α2
α
\alpha
α为粗糙度
∈
[
0
,
1
]
\in[0,1]
∈[0,1]。
n
n
n为宏观平面法线
h
h
h为微观平面法线,即
V
V
V和
I
I
I的中间向量。
当
α
=
1
\alpha=1
α=1时,
D
=
1
π
D = \frac 1 \pi
D=π1,光线向半球面均匀发散; 当
α
=
0
\alpha=0
α=0时,只有当
n
=
h
时
,
D
≠
0
n=h时,D \ne0
n=h时,D=0 。
如果想要理解函数是怎么来的,建议查阅相关论文,作为小白的我,已放弃挣扎~~
代码:
float D =DistributionGGX(N, H, roughness);
根据公式计算D值:
a为
α
\alpha
α
NdotH为
N
和
H
N和H
N和H的点乘,必须大于0。
nom为分子
denom为分母(分母不能为0)。
floatDistributionGGX(Vector3f N, Vector3f H,float roughness){float a = roughness * roughness;float a2 = a * a;float NdotH = std::max(dotProduct(N, H),0.0f);float NdotH2 = NdotH * NdotH;float nom = a2;float denom =(NdotH2 *(a2 -1.0)+1.0);
denom = M_PI * denom * denom;return nom / std::max(denom,0.0000001f);// prevent divide by zero for roughness=0.0 and NdotH=1.0}
几何函数 G
目前较为常用的是其中最为简单的形式,分离遮蔽阴影(Separable Masking and Shadowing Function)。
其中UE4的方案是Schlick-GGX,即基于Schlick近似,将k映射为
k
=
α
2
k = \frac {\alpha}{2}
k=2α ,去匹配GGX Smith方程:
α
=
(
r
o
u
g
h
t
n
e
s
s
+
1
2
)
2
−
−
−
−
−
−
−
−
−
−
−
−
k
=
α
2
−
−
−
−
−
−
−
−
−
−
−
−
−
−
−
−
G
d
i
r
e
c
t
i
o
n
(
n
,
v
,
k
)
=
n
⋅
v
(
n
⋅
v
)
(
1
−
k
)
+
k
−
−
−
−
−
−
−
−
−
−
−
−
−
−
−
−
−
−
−
−
−
G
(
n
,
v
,
l
,
k
)
=
G
v
i
e
w
(
n
,
v
,
k
)
G
l
i
g
h
t
(
n
,
l
,
k
)
\alpha = (\frac {roughtness+1}{2})^2 \\------------\\ k = \frac {\alpha}{2} \\----------------\\ G_{direction}(n, v, k)=\frac{n \cdot v}{(n \cdot v)(1-k)+k} \\---------------------\\ G(n, v, l, k)=G_{view}(n, v, k) G_{light}(n, l, k)
α=(2roughtness+1)2−−−−−−−−−−−−k=2α−−−−−−−−−−−−−−−−Gdirection(n,v,k)=(n⋅v)(1−k)+kn⋅v−−−−−−−−−−−−−−−−−−−−−G(n,v,l,k)=Gview(n,v,k)Glight(n,l,k)
G
(
n
,
v
,
l
,
k
)
G(n, v, l, k)
G(n,v,l,k)返回遮蔽阴影函数的计算结果。
粗糙度
α
=
0
\alpha = 0
α=0时,完全光滑,光线通过率为1.
粗糙度
α
=
1
\alpha = 1
α=1时,最粗糙,光线通过率为
n
⋅
v
n \cdot v
n⋅v
因此
G
(
n
,
v
,
l
,
k
)
∈
[
(
n
⋅
v
)
(
n
⋅
l
)
,
1
]
G(n, v, l, k) \in [(n \cdot v)(n \cdot l),1]
G(n,v,l,k)∈[(n⋅v)(n⋅l),1]
代码:
float G =GeometrySmith(N, V, L, roughness);
N为宏观表面法线
V为光线反射方向(可理解为视口的方向)
L为光线进入入的反方向(可理解为光源的方向)
roughness为粗糙度。
floatGeometrySchlickGGX(float NdotV,float k){float nom = NdotV;float denom = NdotV *(1.0- k)+ k;return nom / denom;}floatGeometrySmith(Vector3f N, Vector3f V, Vector3f L,float roughness){float r =(roughness +1.0);float k =(r * r)/8.0;float NdotV = std::max(dotProduct(N, V),0.0f);float NdotL = std::max(dotProduct(N, L),0.0f);float ggx2 =GeometrySchlickGGX(NdotV, k);float ggx1 =GeometrySchlickGGX(NdotL, k);return ggx1 * ggx2;}
Fresnel方程 F
对于菲涅尔(Fresnel)项,业界方案一般都采用Schlick的Fresnel近似,因为计算成本低廉,而且精度足够:
F
s
c
h
l
i
c
k
(
v
,
h
)
=
F
0
+
(
1
−
F
0
)
(
1
−
(
v
⋅
h
)
)
5
F_{schlick}(v,h) = F_0 + (1-F_0)(1-(v \cdot h))^5
Fschlick(v,h)=F0+(1−F0)(1−(v⋅h))5