前言
前面学习的光栅化(Rasterization)是渲染图像的一种方法,光线追踪(Ray Tracing)则是区别于光栅化的另一种图像渲染方法。那么,为什么完美需要光线追踪呢?原因有以下三点:
1、光栅化渲染的阴影是硬阴影,效果不好;
2、对于Glossy reflection,光栅化渲染出的效果不好;
3、对于间接光照(光线经历多次弹射),光栅化同样得不到好的效果。
而光线追踪则可以完成以上三点。
一、光线追踪基础算法
在正式进入光线追踪前,我们需要了解一些前置知识和定义。
1.1 光线
在图形学中我们定义光线具有以下三个性质(仅针对图形学):
1、光沿直线传播;
2、光线不会发生碰撞;
3、光路可逆性,即光线能从光源到眼睛,那么也能从眼睛到光源。
1.2 光线投射——产生眼光线
受光路可逆性启发,我们能看到物体,说明物体反射了光源的光到我们的眼睛,那么,从我们的眼睛连一条线到物体,就能逆向求出光的传播路径。我们知到,渲染图像就是给每一个像素分配值的过程,那么对于每一个像素,在眼睛(摄像头)和像素之间连一根线,这个线的方向就是观察方向,延长此线,此线会与物体表面存在交点,再在此交点与光源之间连一条线,就得到了光线入射方向,此时我们就有了着色点处的
l
l
l、
v
v
v、
n
n
n,就可以对着色点进行着色了。上述描述的过程叫做Pinhole Camera Model。如下图所示
二、Recursive(Whitted-Style)光线追踪
2.1 算法及建模
Whitted-Style光线追踪即是对Pinhole Camera Model的扩展,在Pinhole Camera Model中,光线只经过了一次反射,同样无法解决间接光照效果不好的问题,因此,Whitted-Style光线追踪就计算了经多次反射和折射的光线,他认为,当光线经过透明物体时,光线除了反射,还会经过折射,因此,同样对于上面的情况,在Whitted-Style光线追踪中,就有四条光路最终到达了我们的眼睛,因此需要对4个着色点进行着色,再将4个点的着色结果求平均后赋给像素,如下所示
2.2 光线与面的交点
在上述建模中,我们知到光源位置,人眼位置、像素位置,要想获得 l l l、 n n n、 v v v进行着色,唯一需要计算的就是着色点的位置,即光线与物体表面的交点的位置。那么如何求解线与面的角点呢,很简单,只要我们知到了光线的方程和面的方程,将二者联立求解即可。
2.2.1 光线方程
光线方程由光线的起点
o
o
o和方向
d
d
d定义,其中
d
d
d为单位向量形式,表示如下
2.2.2 光线与球面的交点
球面方程表示如下,联立球面方程和光线方程即可解得
t
t
t,根据
t
t
t的值可判断有无交点,由几个交点。
2.2.3 光线与三角形的交点
我们知道,物体通常是被分割为一个个的三角形,我们知道每个三角形的坐标,只要与每个三角形求交点(结果为0或1),就能知道光线与物体的交点位置。那么,如何求光线与三角形的交点呢?可分为以下两步:
1、求光线与三角形所在平面的交点;
2、判断此交点是否在三角形内部。
其中,三角形所在平面由法线和一个点定义,表示如下
此外,还有一种方法不必借助平面,可直接求出线与三角形交点的重心坐标,如下所示
2.2.4 轴对齐包围盒(Axis-Aligned Bounding Box-AABB)
在上面的算法中,光线需要与每一个三角形求交点,效率十分低下。因此,我们可以采用Bounding Box来减少运算次数,思想是:用一个大盒子包围一片区域内的三角形,若光线与此区域内的某个三角形相交,那么其必与此盒子相交,如果与盒子不相交,那么与此区域内的三角形一定不相交,因此,我们先判断光线是否和大盒子相交,若相交,再和盒子内部的三角形逐一求交点;若不相交,则不用再与盒子内的三角形求交点了。通常这个盒子我们采用的都是AABB,如下所示
判断光线与AABB有无交点的方法如下:
2.5 AABB划分方式
既然AABB可以减少计算次数,那么应该选择多少个AABB、怎样选取来进行划分呢,总共有三种方式,分别是均匀划分(网格划分)、空间划分和物体划分(BVH),其中BVH是目前最常用的划分方式。
2.5.1 均匀划分
均匀划分方法如下所示
2.5.2 空间划分(Spatial Partitions)
空间划分是指用AABB将空间划分开,每个AABB之间无交集,但AABB内的物体可能有交集,AABB通常表示成二叉树的节点,空间划分最常采用的数据结构是KD-Tree,如下所示
KD-Tree的数据存储结构为:
2.5.3 物体划分(Object Partitions)
物体划分是指用AABB将物体划分开,每个AABB之间可能交集,但AABB内的物体一定没有交集,最常用的方法是BVH(Bounding Volume Hierarchy),构造方法如下所示
数据存储结构如下所示:
算法实现如下:
三、辐射度量学(Radiometry)
在进入辐射度量学正式学习之前,先简要介绍一下将要学习的四个术语及其相互关系,分别是:Radiant Flux, Radiant Intensity, Irradiance,和Radiance。首先我们定义Radiant Energy,Radiant Energy指的是电磁辐射的能量,单位为焦耳(J),如下所示
3.1 Radiant Flux
Radiant flux又称Radiant power,定义为单位时间内的Radiant Energy,如下所示
3.2 Radiant Intensity
3.2.1 单位立体角
在定义Radiant Intensity之前,需要先了解什么是单位立体角,单位立体角是对于球体而言的,可将其类比于圆的弧度,如下所示,可见立体角定义为球面上某区域所占面积与半径平方的比值。
那么,单位立体角自然就是单位面积与半径平方的比值,如下所示
3.2.2 定义
Radiant Intensity定义为单位立体角上的Radiant Flux,如下所示
3.3 Irradiance
Irradiance定义为单位面积上的Radiant Flux,如下所示,与Radiant Intensity对比可知,二者都是由Radiant Flux得来,不过一个是单位立体角上的Power,一个是单位面积上的power。
此外,物体表面的Irradiance还与光线的入射方向以及物体表面法线方向有关,如下所示
3.4 Radiance
Radiance定义为单位投影面积、单位立体角上的Radiance Flux,因此,Radiance也可理解为单位投影面积上的Radiant Intensity,或者单位立体角上的Irradiance,如下所示
四、光线传播
4.1 双向反射分布函数(BRDF)
有了辐射度量学的知识,我们重新审视一下在此视角下的光线传播,光线在某一个点进行反射的过程,实际上就是入射光能量被该点吸收,并将吸收到的Radiance向四周反射的过程。如下所示
由此我们定义双向反射分配函数(BRDF)如下所示
4.2 反射方程
BRDF定义了入射方向为
w
i
w_i
wi,出射方向为
w
r
w_r
wr的反射过程两者之间的比值,有了BRDF,知道了光线的入射方向及其Radiance,就能算出出射方向的Radiance,而在我们的反射过程中,入射方向的光是来自四面八方的,所以,可写出某点处的观测方向为
w
r
w_r
wr的反射方程如下所示
在此方程中,任意一个
L
i
L_i
Li都有可能是另一个入射光线的
L
r
L_r
Lr,因此,这个反射方程是一个递归的方程
4.3 渲染方程
有了反射方程表示某点反射的光,再加上该点自己发的光(若为光源),即可得到渲染方程,如下所示
五、Monte Carlo路径追踪
5.1 Monte Carlo积分
对于如下图所示的很难写出表达式的
f
(
x
)
f(x)
f(x),要想求其定积分也十分困难
因此,Monte Carlo采用估计的方式求此定积分的值,具体方法为:通过随机取样函数值,并计算在此函数值下矩形的面积,将所有面积求和取平均。如下所示
均匀采样下的Monte Carlo积分如下
5.2 路径追踪(Path Tracing)
5.2.1 Whitted Style光线追踪的缺陷
Whitted Style光线追踪的建模过程遵循以下两个特点:
1、光线打到镜面材质上会经历反射、折射;
2、光线打到漫反射材质上就将被全部吸收不再反射。
这两个特点带来了两个问题,一是当光线打到Glossy材质上应该怎样反射呢?二是光线打到漫反射材质上就消失了,无法做到全局光照。如下所示
虽然Whitted Style光线追踪是错误的,但是渲染方程是正确的,再根据Monte Carlo积分,我们就能够求解渲染方程,下面我们就一步步的根据渲染方程和Monte Carlo积分推导出路径追踪。
5.2.2 直接光照下的渲染方程求解
所谓直接光照,即是由光源直接打到摄像头的光或只反射了一次的光,由光源直接打到摄像头的光就是渲染方程中的发光项,由于此项不存在积分,所以在接下来的推导中暂时不考虑此项,反射项此时由于只反射了一次,因此反射项的积分过程中尚不存在递归,因此此时渲染方程反射项表示为
5.2.3 全局光照下的渲染方程求解
全局光照(Global Ilumination)即是在直接光照的基础上,再加上经过多次反射的光,表示在渲染方程中,即使反射项存在了递归的情况,全局光照建模如下
5.2.4 路径追踪
我们已经得到了全局光照下的渲染方程,是不是就可以进行全局光照的渲染了呢?理论上说,是的,但是,如下求解的方程还存在两个问题:
1、随着光线反射次数的增加,光线的数量呈指数级增长;
2、递归的过程缺少终止条件,会无限的递归下去。
接下来我们依次解决这两个问题。
5.2.4.1 光线数量爆炸
对于光线数量爆炸的示意图如下所示,我们发现,要想光线数量不会随着反射次数的增加而呈现指数级增长,N只能取1,即一条出射光线只能对应一条入射光线
但是,这有带来了新的问题,原本来自各个方向的入射光线现在变成了来自1个方向的入射光线,无疑会对我们渲染方程的结果造成巨大的误差,那么如何解决呢,只需要对每一个像素连接多个方向的路径,对每一个路径做路径追踪,再将最终结果取均值即可,如下所示
5.2.4.2 递归终止条件
解决了光线数量爆炸的问题,接下来解决递归缺少终止条件的问题,想象一下在室内空间,光线可能在无限的弹射,因此递归也在无限的进行,这样就没法求解出渲染方程,因此,我们需要一个终止条件,那么,如何引入终止条件呢?
首先,我们引入俄罗斯轮盘赌(RR),设定一个概率P,即你有P的概率存活,1-P的概率死亡,将其引入光线渲染过程,之前,我们对于一个着色点都会发射一条光线,得到结果
L
o
L_o
Lo现在,我们只以P的概率发射一条光线,并且得到结果为
L
o
/
P
L_o/P
Lo/P,在此情况下,期望并不会发生改变,可以说是无偏估计。如下所示
5.2.4.3 采样光源
光线数量爆炸和递归问题都解决了,是不是我们的路径追踪就完美了呢,不是的,其实此时还存在一个小问题,此时我们的路径追踪是从摄像头对一个像素内的区域进行采样连接得到多条路径,但是这样产生的路径经过反射后并不一定能保证反射的光线打到了光源,许多路径就浪费了,利用率十分低,如下所示
因此,我们可以采用采样光源的方法代替采样像素,如下所示