写在前面:

写本系列(自用)的目的是回顾已经学过的知识、记录新学习的知识或是记录心得理解,方便自己以后快速复习,减少遗忘。课程是b站GAMES101-现代计算机图形学入门-闫令琪。

五、光线追踪Ray Tracing

为什么要使用光线追踪?光栅化存在一些问题,例如只能生成硬阴影、全局光照缺失、反射/折射不精准、复杂光照场景乏力等。而光线追踪能带来最真实的渲染结果,但消耗很大。

1、光线投射

(1)光线

首先需要定义光线,光线定义如下:

1、是一条直线;

2、两条光线相交不会产生碰撞;

3、光是可逆的,也就是说如果你能看到某个物体,那么在该物体附近的人也能看到你。

(2)光线投射的流程

光线投射指的是,你在场景中能通过一条光线看到某个点,并且这个点不在阴影内(光源可见)。那么光源->点->你的眼睛就是一条有效光路。那么就可以通过计算这条光路上的能量把该点处的颜色计算出来,完成着色。这就是光线投射。

具体流程是,从眼睛/摄像机投射射线,这条射线分别与每个像素点相交并穿过像素点,打到离摄像机最近的一个点上。若光源能打到该点,将该点与光源相连,并且获得该点处的法线。这就形成了一条光路,可以通过光路对像素点进行光栅化。

涉及到折射/反射的情况如下,有折射/反射时,一个像素点的光能打到多个点。这时需要把打到的所有点的计算结果累加到该像素点上。

(3)交点Ray-Surface Intersection

光线投射需要解决的问题是,从摄像机连接像素打出的光,和物体表面的交点怎么求。首先,定义一个点光源,只需要起点和方向即可定义这条射线。

①隐式表面

光线如何和球求交呢?我们有光线的表达式及球的表达式,那么我们可以直接将光线的表达式代入球的表达式。

将式子展开并整理为二次式,就可以解出根,并根据圆与直线的位置关系求出最终结果。

可以将其推广为任意隐式表面与光线的求交,将光线方程代入隐式表面,求出解。需要注意的是,解需要在物理上有意义,比如不能为虚数。并且解需要是正的。这样就可以求出各种表面和光线的求交。

②显式表面

其实就是光线和三角形求交。可以遍历物体上的三角形,依次求交,但这显然是非常慢的一件事情。

由于三角形肯定在一个平面上,问题就可以简化为我们先让光线和一个平面求交,找到交点后再判断是否在三角形内。首先,我们需要先定义这个平面:选择平面上一个点和法线,就可以定义这个平面。

同样的道理,将光线方程代入平面方程,进行求解即可:

那么,如何在解出交点的同时知道这个交点是否在三角形内呢?可以使用下图的mt算法。假如一个点在三角形内,那么这个点就一定能够写成用三角形重心坐标描述的位置。我们只需要解出t,b1,b2即可。

由于O、D都是三维向量,就相当于我们有三个式子,显然能解出三个未知数。只要满足,t为正数,b1、b2、1-b1-b2均非负,这个点就在三角形内。

求出交点后,我们需要对光线和每个三角形求交的过程加速。加速是必要的事情。

我们可以用包围盒的方式做加速:

③Bounding Volumes包围盒

我们可以用一个包围盒来包裹一个复杂的图形。光线如果碰不到包围盒,就肯定也碰不到盒子物体。对于三维的物体,最常用的包围盒就是一个长方体。

这个长方体就是三个对面形成的包围盒。对面指的是,长方体上下/左右/前后两个面所在的无限大的两个面。我们通常使用轴对齐包围盒AABB,这种长方形都是横平竖直的,是三个对面包围形成的长方形。

如何进行一个AABB和光线的求交呢,先从二维包围盒入手:

对于任意一个光线,我们可以求出一条光线何时和包围盒的两个无限长的相对的线有交点。上图第一张图,和长方形竖着的边求交点,分别有进入时间tmin和退出时间tmax。同理,和水平两条边也有一个tmin和tmax。可以推出图三,在最大的tmin和最小的tmax之间,光线进入了包围盒。

对于三维角度,从逻辑上来说,只有光线进入了三对对面,我们才能说光线进入了包围盒;只要光线离开了任意一对对面,我们就能说光线离开了包围盒。记最大的进入时间为tenter,最小的离开时间为texit。

假如tenter<texit,说明光线在包围盒中停留了一段时间。 接下来考虑负数情况:

如果texit<0,就说明没有交点。

如果texit>=0,tenter<0,说明点在盒子里,有交点。

那么盒子和光线有交点,当且仅当tenter<texit且texit>=0。

(4)AABB应用于光线追踪

①均匀格子

首先在空间中找到物体的包围盒,并将这个包围盒均匀分为大小相等的格子。

标记出有物体的格子(这个标记不包含物体内部,只包含物体边缘)

最后,求出与光线相交的格子,找出那些,既有光线又有物体的格子,在这些格子进行光线和物体的求交,找出光线与物体的第一个交点。当然,格子不能太稀疏也不能太密集。

这种均匀格子的方式在场景中的物体较为集中,场景留白较多的情况下表现一般,当如今仍然有得到应用。

②空间划分

空间划分的思想是:在场景中留白较多(空气较多)的地方少划分,物体多的地方多划分。常见的划分方式有如下三种:

这里主要介绍效果最好的KD-Tree。KD-Tree类似于划分二叉树。

如下图所示,进行KD-Tree预处理。区域A指的是整个正方形区域,区域B指的是,图中除了蓝色以外的部分。由蓝色部分不再继续划分(作为叶子节点)。关于是否继续划分,可以观察划分后的物体数量,例如,假如继续对蓝色部分进行划分,将其划分为两半,划分后其中一个矩形内没有物体,另一个矩形内有物体,不用划分影响也不大,因此就可以选择不再继续划分(选择是否继续划分的条件视具体情况来定)。

在预处理后,就可以进行计算了。对于一条光线,首先判断其与哪些大区域相交,对于下图而言,光线与蓝色区域和区域B均有交点。对于蓝色区域,其没有子节点,所以直接判断蓝色区域内所有物体是否与光线相交。对于区域B,有子节点,那么继续判断光线与B的哪些子节点相交。可以得到光线与绿色区域及区域C都有交点。对于绿色区域,其没有子节点,所以直接判断绿色区域内所有物体是否与光线相交。对于区域C......就这样一直循环直到遍历所有叶子节点。

空间划分的方式也有缺点:对划分的区域以及物体的三角面片并不好求交。一个物体可能出现在多个物体中,这样会导致光线多次与该物体进行计算。

③通过物体划分Bounding Volume Hierarchy(BVH)

首先,确定物体的包围盒,如下所示:

接下来,基于这个包围盒中的物体继续进行划分。可以将物体划分为如下两个包围盒。如图所示,包围盒之间是可能相交的。

继续划分,直到包围盒中的物体数量小到一定程度。

这就是BVH的划分流程,可以总结如下:

那么划分的标准是什么呢。首先我们需要确定划分的轴向,例如沿着x轴、y轴、z轴方向划分,每次都采用与上次不同的划分方向。

除此之外,也可以每次都沿着最长的方向进行划分。例如空间中的物体均沿着x轴方向排布,有物体的区域很长,这样可以每次都以切分x轴的方式划分。

还可以每次都找到位于中间的物体进行划分。

当某块区域的物体数量小于一定值,例如5时停止划分。

划分后的计算步骤与空间划分的步骤相似。

2、辐射度量学Radiometry

辐射度量学说明了如何去描述光照,它定义了一系列的方法和单位,也定义了光一系列的属性。在辐射度量学中,光仍然是直线传播。

(1)Radiant Energy and Flux

辐射能Radiant Energy是电磁辐射的能量,以焦耳为单位计量。

辐射通量Radiant Flux(功率)是单位时间内发射、反射、透射或接收的能量。

(2)重要的光测量参数

此外,还有三种重要的光测量参数:Radiant Intensity辐射强度、Irradiance辐照度、Radiance辐射亮度。

①Radiant Intensity

是点光源发出的、每单位立体角(ω)的功率。

立体角相当于弧度制在三维空间上的延申。我们都知道如何通过弧长和半径定义一个角度,立体角就相当于三维空间中如何通过一块面积来定义一个角度。

知道角度如何求解空间中的面积如下所示:

这样,我们就可以得到总的光强和单位方向的强度:

②Irradiance辐照度

是入射到表面某一点的、每单位垂直(或投影)面积上的功率

之前提到的光强随着传播距离衰减,衰减的其实并不是Intensity而是Irradiance。

③Radiance辐射度

是描述环境中光分布的基本场量,渲染的本质就是计算辐射度。

辐射度(亮度)是指单位立体角、单位投影面积上,表面发射、反射、透射或接收的功率。因此,可以按如下角度理解:

辐射度Radiance就是每单位立体角上的Irradiance;也可以是每单位垂直面积上的Intensity。

此外,从入射的角度理解:如果说是Irradiance是入射到表面某一点的、每单位垂直面积上接受的能量,也就是说Irradiance接收的是来自四面八方的能量;那么Radiance就是指定某个立体角方向入射到某一点的方向。

从发出的角度理解:Radiance就是指定发出到某个立体角的能量。

Radiance无非就是在Irradiance的基础上加上了方向。Irradiance是将来自四面八方的Radiance积起来。

3、BRDF

BRDF能定义一个概念:从某个方向进来并且反射到某个方向去,这部分能量应该是多少。以及从某个方向进来,会往不同的方向怎么去分散,分散多少能量。

反射可以换一种理解方式:光线打到一个物体表面,被吸收了,然后物体再将这部分能量发射出来。这就可以转换到Radiance和Irradiance上去。

(1)反射方程

对于一个小的块能接收多少能量呢?考虑某一个方向,将它包含的所有立体角的Radiance积起来就能得到该方向的Irradiance。

对于任意一个出射方向,算出它的Radiance,然后除以这么一个小块接收到的Irradiance,这就是BRDF的定义。

对于每一个入射方向, 都会对应入射方向、着色点,出射方向这样一个BRDF,BRDF会告诉我们从某一个方向进来的光反射到出射方向有多强。那么把每一个方向对出射方向的贡献都加起来,就可以得到一个点在所有入射方向的共享下,看起来会是什么样的。如下式所示,这就是反射方程。

但反射方程只考虑了来自光源的Irradiance,如果旁边有其他物体,那么它们反射光的Radiance也能作为入射光照亮一个点。也就是说这是个递归的过程。

(2)渲染方程

一个物体可能不止会反射光,还会发光。反射方程+自身发光就是渲染方程。所有的光线传播都能用这个简单的公式来总结。

光栅化只能计算直接光照和一次弹射。而光线追踪可以继续计算后续的二次、三次弹射(后面补)。如果一直弹射下去,全局光照就会收敛到一个亮度,不再继续变量。

4、蒙特卡洛

蒙特卡洛用于求解难以解出的定积分。例如图下的函数,许多函数的解析式利用我们在微积分上学到的求解方法是很难求解的,我们就需要直接求解其定积分。

蒙特卡洛的思想是,在区间a,b中随机取点x,得到该点对应的f(x)值,假设这个函数就是长为b-a,宽为f(x)的长方形,求这个长方形的面积。只要我们取足够多的点,最终把所有的长方形面积求和并取平均,就可以得到我们想要求解的定积分值。如下所示:

对于蒙特卡洛来说,采样点越多,越精确。并且,在x上采样就必须在x上积分。

5、路径追踪

之前提到的光线追踪有几个问题/缺点。

问题1,Whitted-style假设反射是完美的,没有任何模糊或扩散,但是对于glossy表面,它与镜面不同,现实中光线会在一个范围内散射,而不是单一方向的镜面反射。

Whitted-style在光打到漫反射物体后就停了,不会再继续反射。但实际上,漫反射物体会将光往四面八方进行反射。

因此Whitted-style是错的,但是渲染方程是对的。那么我们就需要计算出渲染方程。

首先求解直接光照,考虑一个简单情况。首先只考虑一个点,对于这个场景,可能会有物体挡住光,有相对较大的面光源,涉及到观测方向以及各个不同入射方向进来的光。

假设这个点不发光,由于是计算直接光照,也就是说其他的物体不会反射光,该点接收到的光只可能是直接光照。

那就可以利用蒙特卡洛来求解。要使用蒙特卡洛,需要知道随机一个方向(随机变量),它对应的f(x)和pdf都是多少。f(x)就是我们要进行积分的项,pdf我们可以先考虑均匀采样的情况,如下

这样,我们就可以将,四面八方的光源对一个点的出射方向o的直接光照的贡献,写作一个算法。

算法如下:考虑出射方向为W0的着色点p,从着色点向半球随机发出n个不同方向,对于任何一个选中的方向,从这个方向连出一条光线来,如果这条光线打到了光源,就利用蒙特卡洛计算其积分。这就是路径追踪。

但我们还需要加入间接光照。如下,Q点反射的光也会打到P点。我们可以将此时的情况看作,从P点观察Q,Q点的直接光照是多少。由于光源打到Q点,Q的直接光照就可以进行计算,我们可以得到Q反射到P的光照。

那么可以继续对路径追踪算法进行完善:

这里增加了一条分支,如果打到了物体,就递归地去计算物体的直接光照。但这样算,由于我们打出的是n根光线,光线的数量会爆炸,如下:

解决方式是,我们只从着色点打出一根光线。这也是为什么叫路径追踪的原因

当然,使用一根光线效果会大打折扣,我们可以通过从一个像素点打出多个path来解决这个问题。

路径追踪如下,从一个像素点打出n根光线,如果光线打到了场景中某个位置,就对这个位置做着色。

但着色处还存在问题:我们并没有定义递归什么时候停止。

为了解决这个问题,需要引入俄罗斯轮盘赌:
 

我们可以提前定好一个概率,以一定的概率p打出一条光线,最后将结果除以p。在1-p的概率内不打出这条光线。这样计算得到的期望是对的。

继续对着色优化如下:

目前还存在一个小问题,如果从一个像素点打出的光线少,那么最后的结果就容易走样;如果打出的光线多,那么结果就很干净。显然目前的算法并不高效。

算法不高效的原因是:我们目前使用的是均匀采样,随机向周围打出光线,很多光线其实根本不能打到物体,这就造成了严重的浪费。

如果我们直接在光源上采样,那么采样点都不会被浪费了。蒙特卡洛要求,在哪采样就在哪积分,因此,需要将原本在球面上的积分转化为在光源上的积分。

我们可以利用换元法,完成这件事:

这样,我们就可以把渲染方程重写并利用蒙特卡洛进行求解:

对光源而言形象的采样方法如下:

这样,算法可以改写为:

最后,还有一个小问题:光源是可能被遮挡的,因此我们在采样光源时,还需要判断光源是否能打到物体,只有能打到物体的光才能参与计算。

如果遇到点光源,建议把它作为一个很小的面积光源。路径追踪不好处理点光源。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐