光栅化无法实现软阴影、磨砂镜面反射、间接照明、光线弹射不止一次的情况

光栅化的速度很快,但是质量并不高;光线追踪是非常准确的,但是非常缓慢。所以一般情况下,光栅化是实时的 real-time,光线追踪是离线的 offline。
一帧画面用光线追踪来渲染需要一万个 CPU 小时(10K CPU core hours)


求到第一个交点后,连接交点与光源得到另一个光线

判断新的光线与光源有没有被阻挡,如果有那么就在阴影里.

每个像素的颜色由反射经过的所有点提供,各个点占比不同.

光线由初始位置与方向向量来定义

Ray Equation:
r
(
t
)
=
o
+
t
d
0
⩽
t
<
∞
\mathbf{r}(t)=\mathbf{o}+t\mathbf{d}\quad 0\leqslant t<\infin
r(t)=o+td0⩽t<∞
Ray: r ( t ) = o + t d 0 ⩽ t < ∞ \mathbf{r}(t)=\mathbf{o}+t\mathbf{d}\quad 0\leqslant t<\infin r(t)=o+td0⩽t<∞
Sphere: p : ( p − c ) 2 − R 2 = 0 \mathbf{p}:(\mathbf{p}-\mathbf{c})^2-R^2=0 p:(p−c)2−R2=0
Solve for intersection: ( o + t d − c ) 2 − R 2 = 0 (\mathbf{o}+t\mathbf{d}-\mathbf{c})^2-R^2=0 (o+td−c)2−R2=0

a
t
2
+
b
t
+
c
=
0
,
where
a
=
d
⋅
d
b
=
2
(
o
−
c
)
⋅
d
c
=
(
o
−
c
)
⋅
(
o
−
c
)
−
R
2
t
=
−
b
±
b
2
−
4
a
c
2
a
Ray:
r
(
t
)
=
o
+
t
d
,
0
≤
t
<
∞
\mathbf{r}(t)=\mathbf{o}+t \mathbf{d}, 0 \leq t<\infty
r(t)=o+td,0≤t<∞
General implicit surface: p : f ( p ) = 0 \mathbf{p}: f(\mathbf{p})=0 p:f(p)=0
Substitute ray equation: f ( o + t d ) = 0 f(\mathbf{o}+t \mathbf{d})=0 f(o+td)=0
Solve for real, positive roots

判断光线与每个三角形是否相交
Ray equation: r ( t ) = o + t d , 0 ≤ t < ∞ \mathbf{r}(t)=\mathbf{o}+t \mathbf{d}, 0 \leq t<\infty r(t)=o+td,0≤t<∞
Plane equation: p : ( p − p ′ ) ⋅ N = 0 \mathbf{p}: (\mathbf{p}-\mathbf{p}^{'})\cdot \mathbf{N}=0 p:(p−p′)⋅N=0
Solve for intersection:
S
e
t
p
=
r
(
t
)
a
n
d
s
o
l
v
e
f
o
r
t
(
p
−
p
′
)
⋅
N
=
(
o
+
t
d
−
p
′
)
⋅
N
=
0
t
=
(
p
′
−
o
)
⋅
N
d
⋅
N
Check:
0
≤
t
<
∞
Set \ \mathbf{p}=\mathbf{r}(t) \ and \ solve\ for\ t \\
Möller Trumbore Algorithm 是一种更快更便捷更直观的求解交点方式
O
→
+
t
D
→
=
(
1
−
b
1
−
b
2
)
P
→
0
+
b
1
P
→
1
+
b
2
P
→
2
\overrightarrow{\mathbf{O}}+t \overrightarrow{\mathbf{D}}=\left(1-b_{1}-b_{2}\right) \overrightarrow{\mathbf{P}}_{0}+b_{1} \overrightarrow{\mathbf{P}}_{1}+b_{2} \overrightarrow{\mathbf{P}}_{2}
O+tD=(1−b1−b2)P0+b1P1+b2P2
[
t
b
1
b
2
]
=
1
S
→
1
∙
E
→
1
[
S
→
2
⋅
E
→
2
S
→
1
⋅
S
→
S
→
2
∙
D
→
]
\left[
其中
E
→
1
=
P
→
1
−
P
→
0
E
→
2
=
P
→
2
−
P
→
0
S
→
=
O
→
−
P
→
0
S
→
1
=
D
→
×
E
→
2
S
→
2
=
S
→
×
E
→
1
解出来后,需要进行验证:
t
>
0
,
b
1
⩾
0
,
b
2
⩾
0
,
b
3
⩾
0
t>0,b_1\geqslant 0,b_2\geqslant 0,b_3\geqslant 0
t>0,b1⩾0,b2⩾0,b3⩾0
如果对每个三角形都检测是否相交,速度会非常的慢。现在引入 Bounding Volumes 的方法,它使一个物体完全被包含,如果光线没有穿过 Bounding Volumes,那么光线也不可能穿过物体。

通常,我们用轴对齐的盒子,Axis-Aligned Bounding Box (AABB) (轴对齐包围盒),包围盒的任何一面都沿 x , y x,y x,y 或 z z z 轴。

对于 2D 情形,两个对面 (沿一个轴方向的两个面称为一个对面) 线段的交集即为进入物体的线段。

也可以认为,进入所有对面的最晚时间到出所有对面的最早时间为光线进入物体的时间。
所以三维情况同理,有
t
e
n
t
e
r
=
m
a
x
{
t
m
i
n
}
,
t
e
x
i
t
=
m
i
n
{
t
m
a
x
}
t_{enter} = max\{t_{min}\}, t_{exit} = min\{t_{max}\}
tenter=max{tmin},texit=min{tmax}
如果
t
e
n
t
e
r
<
t
e
x
i
t
t_{enter} < t_{exit}
tenter<texit ,那么光线也不一定进入了物体:
总结,光线与包围盒相交当且仅当 t e n t e r < t e x i t ∧ t e x i t ⩾ 0 t_{enter} < t_{exit} \ \land \ t_{exit}\geqslant 0 tenter<texit ∧ texit⩾0.
为什么要用轴对齐包围盒?
" width="200">
利用分量即可计算出 t t t :
t = p x ′ − o x d x t=\frac{\mathbf{p}_{x}^{\prime}-\mathbf{o}_{x}}{\mathbf{d}_{x}} t=dxpx′−ox
找到包围盒

生成网格

将每个物体边界所在的网格标记

按光线方向顺序遍历网格,对每个网格测试与网格内所有物体的相交情况

如果我们用非常稀疏的网格,我们需要对很多物体进行相交判断;如果我们用非常密的网格,我们需要对很多网格进行判断。所以如何确定网格是决定速度的十分关键的问题。

八叉树预处理就是对于每个三维空间,切三刀变成八分,然后对每一份继续切.



KD-Trees 数据结构
内部节点存储:
叶子节点:
实现加速的过程:

如何判断一个三角形是否与一个包围盒有交点是比较难实现的;一个物体可能出现在多个叶子结点里.
空间划分的结果可能导致一个物体可能出现在多个叶子结点里,所以这里我们引入物体划分来解决这个问题

怎样进行合理的分割呢?
BVHs 数据结构
内部节点存储:
叶子节点:
Intersect(Ray ray, BVH node) {
if (ray misses node.bbox) return;
if (node is a leaf node)
test intersection with all objs;
return closest intersection;
hit1 = Intersect(ray, node.child1);
hit2 = Intersect(ray, node.child2);
return the closer of hit1, hit2;
}

这种方法不用判断三角形是否与一个包围盒有交点,实现起来比较容易.
BVH 方法一般按照物体数量来均等划分,即节点上物体的数量都相等,但是遇到一些特殊情况:

显然,有更好的划分方法:

那么,我们是如何评估划分的好坏的呢?我们用找到最近相交点所耗费的时间来评估。
对于叶子结点,耗费时间
C
C
C 满足
C
=
∑
i
=
1
N
C
i
s
e
c
t
(
i
)
=
N
C
i
s
e
c
t
C=\sum^N_{i=1}C_{isect}(i)=NC_{isect}
C=i=1∑NCisect(i)=NCisect
其中
C
i
s
e
c
t
(
i
)
C_{isect}(i)
Cisect(i) 是得到物体
i
i
i 与光线交点所需时间。对于非叶子结点
C
=
C
t
r
a
v
+
p
A
C
A
+
p
B
C
B
C=C_{trav}+p_AC_A+p_BC_B
C=Ctrav+pACA+pBCB
其中
C
t
r
a
v
C_{trav}
Ctrav 是穿过一个内部节点所耗费的时间,
C
A
C_A
CA 与
C
B
C_B
CB 是找到与左右子树最近相交点所耗费时间,
p
A
p_A
pA 与
p
B
p_B
pB 是光线与左右孩子结点相交的概率。
这里如果要计算
C
C
C 并得到其全局最优解,就需要后序遍历出它所有子节点的
S
A
H
\mathrm{SAH}
SAH 值,这目前是不可行的,所以我们用估计的局部最优解来代替,这种方法称为局部贪婪的
S
A
H
\mathrm{SAH}
SAH
C
=
C
t
r
a
v
+
p
A
N
A
C
i
s
e
c
t
+
p
B
N
B
C
i
s
e
c
t
C=C_{trav}+p_AN_AC_{isect}+p_BN_BC_{isect}
C=Ctrav+pANACisect+pBNBCisect
一次划分把物体集合
N
N
N 划分为集合
A
A
A 与集合
B
B
B,
S
N
,
S
A
,
S
B
S_N,S_A,S_B
SN,SA,SB 是总集合和两个集合的包围盒表面积,
N
A
,
N
B
N_A,N_B
NA,NB 是两个集合中物体的数量。
对于物体
A
A
A 与物体
B
B
B,光线穿过
B
B
B 的同时穿过
A
A
A 的概率可以用它们的表面积定义
P
(
h
i
t
A
∣
h
i
t
B
)
=
S
A
S
B
P(\mathrm{hit}\ A|\mathrm{hit}\ B)=\frac{S_A}{S_B}
P(hit A∣hit B)=SBSA
最后得到 Surface Area Heuristic (SAH):
C
=
C
t
r
a
v
+
S
A
S
N
N
A
C
i
s
e
c
t
+
S
B
S
N
N
B
C
i
s
e
c
t
C=C_{\mathrm{trav}}+\frac{S_{A}}{S_{N}} N_{A} C_{\mathrm{isect}}+\frac{S_{B}}{S_{N}} N_{B} C_{\mathrm{isect}}
C=Ctrav+SNSANACisect+SNSBNBCisect
其中,一次划分把物体集合
N
N
N 划分为集合
A
A
A 与集合
B
B
B,
S
N
,
S
A
,
S
B
S_N,S_A,S_B
SN,SA,SB 是总集合和两个集合的包围盒表面积,
N
A
,
N
B
N_A,N_B
NA,NB 是两个集合中物体的数量。

那么如何进行划分的采样呢,我们知道只有当 N A , N B N_A,N_B NA,NB 发生改变时, C C C 才会改变,所以我们只考虑使 N A , N B N_A,N_B NA,NB 改变了的所有划分
根据物体质心的位置判断物体属于那一划分区,但是如果我们有 N N N 个物体,那么沿三个轴划分就有 3 N − 3 3N-3 3N−3 个平面,这样的开销太大,得不偿失。

我们可以用一种高效的近似方法,把物体空间划分为 B B B 个桶( B B B 一般比较小: B < 32 B<32 B<32)
算法如下:
For each axis: x,y,z:
initialize buckets
For each primitive p in node:
b = compute_bucket(p.centroid) # 计算物体p在哪一个桶b中
b.bbox.union(p.bbox); # 桶b与物体p的包围盒结合
b.prim_count++; # 桶b物体数加一
For each of the B-1 possible partitioning planes evaluate SAH
Execute lowest cost partitioning found (or make node a leaf)
电磁辐射的能量。单位为焦耳,用符号表示:
Q
[
J
=
J
o
u
l
e
]
Q[J=Joule]
Q[J=Joule]
单位时间内发射、反射、传输或接收的能量:
Φ
≡
d
Q
d
t
[
W
=
W
a
t
t
]
[
lm
=
lumen
]
∗
\Phi \equiv \frac{\mathrm{d} Q}{\mathrm{~d} t}[\mathrm{~W}=\mathrm{Watt}][\operatorname{lm}=\text { lumen }]^{*}
Φ≡ dtdQ[ W=Watt][lm= lumen ]∗
点光源发出的单位立体角的功率

I ( ω ) ≡ d Φ d ω [ W s r ] [ lm s r = c d = candela ] I(\omega) \equiv \frac{\mathrm{d} \Phi}{\mathrm{d} \omega}\\ \left[\frac{\mathrm{W}}{\mathrm{sr}}\right]\left[\frac{\operatorname{lm}}{\mathrm{sr}}=\mathrm{cd}=\text { candela }\right] I(ω)≡dωdΦ[srW][srlm=cd= candela ]

Ω = A r 2 \Omega=\frac{A}{r^2} Ω=r2A
一个球体的立体角为 4 π 4\pi 4π steradiance.

d
A
=
(
r
d
θ
)
⋅
(
r
sin
θ
d
ϕ
)
=
r
2
sin
θ
d
θ
d
ϕ
dA=(rd\theta)\cdot(r\sin\theta d\phi)=r^2\sin\theta d\theta d\phi
dA=(rdθ)⋅(rsinθdϕ)=r2sinθdθdϕ
微分立体角:
d
ω
=
d
A
r
2
=
sin
θ
d
θ
d
ϕ
d\omega = \frac{dA}{r^2}=\sin\theta d\theta d\phi
dω=r2dA=sinθdθdϕ

Φ
=
∫
S
2
I
d
ω
=
4
π
I
I
=
Φ
4
π
\Phi =\int_{S^{2}} I \mathrm{~d} \omega =4 \pi I \\ I =\frac{\Phi}{4 \pi}
Φ=∫S2I dω=4πII=4πΦ

辐射照度是每单位面积上入射到一个表面点上的功率。
E
(
x
)
≡
d
Φ
(
x
)
d
A
[
W
m
2
]
[
lm
m
2
=
lux
]
E(\mathbf{x}) \equiv \frac{\mathrm{d} \Phi(\mathbf{x})}{\mathrm{d} A} \\ {\left[\frac{\mathrm{W}}{\mathrm{m}^{2}}\right]\left[\frac{\operatorname{lm}}{\mathrm{m}^{2}}=\operatorname{lux}\right]}
E(x)≡dAdΦ(x)[m2W][m2lm=lux]

实际上,
A
A
A 并不是照射面积,而是照射的有效面积,即需要求投影
E
=
Φ
A
cos
θ
,
θ
=
<
l
,
n
>
E=\frac{\Phi}{A\cos \theta}, \quad \theta =

随着距离的增加,辐射强度是不变的(立体角不变),辐射照度在逐渐衰减
辐射亮度是描述光在环境中的分布的基本场量,渲染完全是关于辐射亮度的计算的。
辐射亮度是一个表面每单位立体角、每投影单位面积发射、反射、透射或接收的功率。

L ( p , ω ) ≡ d 2 Φ ( p , ω ) d ω d A cos θ [ W s r m 2 ] [ c d m 2 = lm s r m 2 = nit ] L(\mathrm{p}, \omega) \equiv \frac{\mathrm{d}^{2} \Phi(\mathrm{p}, \omega)}{\mathrm{d} \omega \mathrm{d} A \cos \theta} \\ {\left[\frac{\mathrm{W}}{\mathrm{sr}\ \mathrm{m}^{2}}\right]\left[\frac{\mathrm{cd}}{\mathrm{m}^{2}}=\frac{\operatorname{lm}}{\mathrm{sr\ m}^{2}}=\text { nit }\right]} L(p,ω)≡dωdAcosθd2Φ(p,ω)[sr m2W][m2cd=sr m2lm= nit ]
入射辐射亮度是到达表面的每单位立体角的辐射照度

L ( p , ω ) = d E ( p ) d ω cos θ L(\mathrm{p}, \omega)=\frac{\mathrm{d} E(\mathrm{p})}{\mathrm{d} \omega \cos \theta} L(p,ω)=dωcosθdE(p)
出射表面辐射亮度是每单位投影面积离开表面的辐射强度

L ( p , ω ) = d I ( p , ω ) d A cos θ L(\mathrm{p}, \omega)=\frac{\mathrm{d} I(\mathrm{p}, \omega)}{\mathrm{d} A \cos \theta} L(p,ω)=dAcosθdI(p,ω)
辐射照度:面元 d A \mathrm dA dA 单位面积接受的所有方向 (半球) 上的所有能量
辐射亮度:面元
d
A
\mathrm dA
dA 单位面积从“方向”
d
ω
\mathrm d\omega
dω 接受到的能量
d
E
(
p
)
=
L
i
(
p
,
ω
)
cos
θ
d
ω
E
(
p
)
=
∫
H
2
L
i
(
p
,
ω
)
cos
θ
d
ω
\mathrm d E(\mathrm{p}) =L_{i}(\mathrm{p}, \omega) \cos \theta \mathrm{d} \omega \\ E(\mathrm{p}) =\int_{H^{2}} L_{i}(\mathrm{p}, \omega) \cos \theta \mathrm{d} \omega
dE(p)=Li(p,ω)cosθdωE(p)=∫H2Li(p,ω)cosθdω
单位半球:
H
2
H^2
H2


方向 $\omega $ 的辐射亮度被 d A \mathrm dA dA 接受并转化为辐射照度 E E E,再转换为任意方向的辐射亮度 L L L.
入射微分辐射照度: d E ( ω i ) = L ( ω i ) cos θ i d ω i dE(\omega_i)=L(\omega_i)\cos \theta_i d\omega_i dE(ωi)=L(ωi)cosθidωi
出射微分辐射亮度: d L r ( ω r ) dL_r(\omega_r) dLr(ωr)
双向反射分布函数 BRDF 表示从每个入方向反射到每个出方向的光的数量。

f r ( ω i → ω r ) = d L r ( ω r ) d E i ( ω i ) = d L r ( ω r ) L i ( ω i ) cos θ i d ω i [ 1 s r ] f_{r}\left(\omega_{i} \rightarrow \omega_{r}\right)=\frac{\mathrm{d} L_{r}\left(\omega_{r}\right)}{\mathrm{d} E_{i}\left(\omega_{i}\right)}=\frac{\mathrm{d} L_{r}\left(\omega_{r}\right)}{L_{i}\left(\omega_{i}\right) \cos \theta_{i} \mathrm{~d} \omega_{i}}\left[\frac{1}{\mathrm{sr}}\right] fr(ωi→ωr)=dEi(ωi)dLr(ωr)=Li(ωi)cosθi dωidLr(ωr)[sr1]

L r ( p , ω r ) = ∫ H 2 f r ( p , ω i → ω r ) L i ( p , ω i ) cos θ i d ω i L_{r}\left(\mathrm{p}, \omega_{r}\right)=\int_{H^{2}} f_{r}\left(\mathrm{p}, \omega_{i} \rightarrow \omega_{r}\right) L_{i}\left(\mathrm{p}, \omega_{i}\right) \cos \theta_{i} \mathrm{~d} \omega_{i} Lr(p,ωr)=∫H2fr(p,ωi→ωr)Li(p,ωi)cosθi dωi
加上一个自身发出的光的项,得到渲染方程
L
r
(
p
,
ω
r
)
=
L
e
(
p
,
ω
o
)
+
∫
Ω
+
L
i
(
p
,
ω
i
)
f
r
(
p
,
ω
i
,
ω
r
)
(
n
⋅
ω
i
)
d
ω
i
L_{r}\left(\mathrm{p}, \omega_{r}\right)=L_e(\mathrm p, \omega_o)+\int_{\Omega+} L_{i}\left(\mathrm{p}, \omega_{i}\right)f_{r}\left(\mathrm{p}, \omega_{i} , \omega_{r}\right) (n\cdot \omega_i)\mathrm{~d} \omega_{i}
Lr(p,ωr)=Le(p,ωo)+∫Ω+Li(p,ωi)fr(p,ωi,ωr)(n⋅ωi) dωi
假设所有方向都指向外部

其中 K ( u , v ) K(u,v) K(u,v) 是光线传输算子、反射操作符

L
=
E
+
K
L
I
L
−
K
L
=
E
(
I
−
K
)
L
=
E
L
=
(
I
−
K
)
−
1
E
L
=
(
I
+
K
+
K
2
+
K
3
+
…
)
E
L
=
E
+
K
E
+
K
2
E
+
K
3
E
+
…

这样处理后,得到的 L L L 可以看作不同光线弹射次数的分解
在这里,我们忽略物体本身发光,即渲染方程为
L
r
(
p
,
ω
r
)
=
∫
Ω
+
L
i
(
p
,
ω
i
)
f
r
(
p
,
ω
i
,
ω
r
)
(
n
⋅
ω
i
)
d
ω
i
L_{r}\left(\mathrm{p}, \omega_{r}\right)=\int_{\Omega+} L_{i}\left(\mathrm{p}, \omega_{i}\right)f_{r}\left(\mathrm{p}, \omega_{i} , \omega_{r}\right) (n\cdot \omega_i)\mathrm{~d} \omega_{i}
Lr(p,ωr)=∫Ω+Li(p,ωi)fr(p,ωi,ωr)(n⋅ωi) dωi
由蒙特卡洛方法求积分,我们知道
∫
a
b
f
(
x
)
d
x
≈
1
N
∑
k
=
1
N
f
(
X
j
)
p
(
X
k
)
X
∼
(
x
)
\int_a^bf(x)\mathrm dx \approx \frac{1}{N}\sum_{k=1}^N\frac{f(X_j)}{p(X_k)}\qquad X\sim (x)
∫abf(x)dx≈N1k=1∑Np(Xk)f(Xj)X∼(x)
所以,我们可以这样计算渲染方程
L
o
(
p
,
ω
o
)
=
∫
Ω
+
L
i
(
p
,
ω
i
)
f
r
(
p
,
ω
i
,
ω
o
)
(
n
⋅
ω
i
)
d
ω
i
≈
1
N
∑
i
=
1
N
L
i
(
p
,
ω
i
)
f
r
(
p
,
ω
i
,
ω
o
)
(
n
⋅
ω
i
)
p
(
ω
i
)

计算的算法采用递归思想,如果我们沿某个方向发射光线,打在光源上,那么正常处理;如果打在物体上,递归调用。
shade(p, wo)
Randomly choose N directions wi~pdf
Lo = 0.0
For each wi
Trace a ray r(p, wi)
If ray r hit the light
Lo += (1 / N) * L_i * f_r * cosine / pdf(wi)
Else If ray r hit an object at q
Lo += (1 / N) * shade(q, -wi) * f_r * cosine / pdf(wi)
Return Lo
但是上面算法并不合适,如果我们每次计算光线需要 N N N 次采样,如果由 r r r 条光线要计算,那么就要进行 N N N 次才样。这种指数级的增长是不合理的,因此我们取 N = 1 N=1 N=1 来解决这个问题:
shade(p, wo)
Randomly choose ONE direction wi~pdf(w)
Trace a ray r(p, wi)
If ray r hit the light
Return L_i * f_r * cosine / pdf(wi)
Else If ray r hit an object at q
Return shade(q, -wi) * f_r * cosine / pdf(wi)
但是如果只做一次采样必然会造成非常大的噪声,因此我们选择多条路径来计算

只需通过每个像素跟踪更多路径,并平均它们的亮度
ray_generation(camPos, pixel)
Uniformly choose N sample positions within the pixel
pixel_radiance = 0.0
For each sample in the pixel
Shoot a ray r(camPos, cam_to_sample)
If ray r hit the scene at p
pixel_radiance += 1 / N * shade(p, sample_to_cam)
Return pixel_radiance
但是,在 shade 函数中,我们进行了递归调用,所以需要设置好递归边界,即什么时候要进行弹射。因为实际上,光线是无限进行弹射的,但是计算机当然不能完全实现,所以需要一种方法来确定递归边界,并且让结果合理。
用俄罗斯轮盘赌 Russian Roulette (RR) 方法来解决这个问题:规定由概率
P
P
P 会弹射光线,并且返回的辐射亮度为
L
o
/
P
Lo/P
Lo/P ,那么就有
1
−
P
1-P
1−P 的概率返回 0;这样的话我们最终的期望会是
E
=
P
×
L
o
/
P
+
(
1
−
P
)
×
0
=
L
o
E=P\times Lo / P+(1-P)\times 0=Lo
E=P×Lo/P+(1−P)×0=Lo
在大数定律下,当路径足够多时,结果仍然是正确的,同时也有了递归边界。
shade(p, wo)
Manually specify a probability P_RR
Randomly select ksi in a uniform dist. in [0, 1]
If (ksi > P_RR) return 0.0;
Randomly choose ONE direction wi~pdf(w)
Trace a ray r(p, wi)
If ray r hit the light
Return L_i * f_r * cosine / pdf(wi) / P_RR
Else If ray r hit an object at q
Return shade(q, -wi) * f_r * cosine / pdf(wi) / P_RR
如果我们在半球上均匀采样,如果光源很小,那么有可能“浪费”很多采样,因为可能仅有几条光线会到达光源。为了解决这个问题,我们改为在光源进行采样

对光源均匀采样,此时
p
d
f
=
1
/
A
pdf=1/A
pdf=1/A,但是渲染方程中是对立体角进行积分,所以我们要找到光源面元
d
A
\mathrm dA
dA 与立体角
d
ω
\mathrm d\omega
dω 间的关系。由上图我们可以得到
d
ω
=
d
A
cos
θ
′
∣
∣
x
′
−
x
∣
∣
2
\mathrm d \omega =\frac{\mathrm dA\cos \theta^{'}}{||x^{'}-x||^2}
dω=∣∣x′−x∣∣2dAcosθ′
所以渲染方程改写为
L
o
(
x
,
ω
o
)
=
∫
Ω
+
L
i
(
x
,
ω
i
)
f
r
(
x
,
ω
i
,
ω
o
)
cos
θ
d
ω
i
=
∫
A
L
i
(
x
,
ω
i
)
f
r
(
x
,
ω
i
,
ω
o
)
cos
θ
cos
θ
′
∥
x
′
−
x
∥
2
d
A
在之前,我们认为光线是“意外地”在半球的采样上照射到了光源,但是引入这个方法后,我们认为辐射亮度来自两个部分:
shade(p, wo)
# Contribution from the light source.
Uniformly sample the light at x’ (pdf_light = 1 / A)
L_dir = L_i * f_r * cos θ * cos θ’ / |x’ - p|^2 / pdf_light
# Contribution from other reflectors.
L_indir = 0.0
Test Russian Roulette with probability P_RR
Uniformly sample the hemisphere toward wi (pdf_hemi = 1 / 2pi)
Trace a ray r(p, wi)
If ray r hit a non-emitting object at q
L_indir = shade(q, -wi) * f_r * cos θ / pdf_hemi / P_RR
Return L_dir + L_indir
但是如果着色点与光源之间正好被某个物体挡住了怎么处理

只需要增加一个判断即可
shade(p, wo)
# Contribution from the light source.
L_dir = 0.0
Uniformly sample the light at x’ (pdf_light = 1 / A)
Shoot a ray from p to x’
If the ray is not blocked in the middle
L_dir = L_i * f_r * cos θ * cos θ’ / |x’ - p|^2 / pdf_light
# Contribution from other reflectors.
L_indir = 0.0
Test Russian Roulette with probability P_RR
Uniformly sample the hemisphere toward wi (pdf_hemi = 1 / 2pi)
Trace a ray r(p, wi)
If ray r hit a non-emitting object at q
L_indir = shade(q, -wi) * f_r * cos θ / pdf_hemi / P_RR
Return L_dir + L_indir

左图是相机拍摄的真实场景,右图是全局光照的路径追踪,可以说…几乎百分之百正确!