GAMES101:现代计算机图形学入门-笔记-06
从今天开始正式进入光线追踪的部分,由于关于之前四篇笔记的总结还没有写完,我们暂时先写06的笔记。

为什么要光线追踪?当然是因为光栅化的过程依然存在一些问题,比如光栅化基本都只针对点光源,只考虑光线只反射一次的情况,无法处理软阴影,那在这个过程中我们就需要使用光线追踪来提高我们的成像效果。
值得一提的是,目前为止的光线追踪对计算性能要求都比较高,在针对较大的地图场景时,很多时候出于GPU计算性能的考量会有意降低渲染质量。

我们提到渲染时总会提到实时渲染,也就是只有运行的时候才会进行渲染,但是光线追踪往往是在线下的时候进行,且速度非常缓慢。
Basic Ray-Tracing Algorithm

从最基础的光线追踪算法开始:

关于光线的设定:光都是沿直线传播,光线不会产生碰撞,光从光源直接传播到眼睛。当然在实际的生活中,我们知道其实这些设定其实全部都是错的(第三个除外)。而所谓的光线追踪中,我们就要利用光线的可逆性来从眼睛追溯到光源。
“你凝视深渊的时候深渊也在凝视你”就是对光线最质朴的表述。
如何在光线追踪中生成一条光线?

我们设定我们的眼睛是一个像素点,然后连接眼睛和成像平面上的一个像素点得到一条射线,我们叫做eye ray,eye ray与场景会产生一个交点,然后我们需要从这个点与光源进行连线,我们称为shadow ray,如果shadow ray之间存在阻挡说明这个交点处于阴影之下。否则我们就计算这个点的着色之后照常。
非常简单的逻辑,简单到还是只考虑了光线反射了一次,且光线都是完美反射的情况。显然我们需要更复杂,更符合逻辑的算法,于是在1979年,一个递归算法诞生了:
Recursive (Whitted-Style) Ray Tracing


显然,在同时考虑光线的折射与反射后,这个场景需要计算的东西就显著变多了。那这个Recursive Ray Tracing算法的本质其实就是将所有光路与物体的交点都纳入考虑,将所有交点和光源都进行连线,然后再检测这些连线中是否有物体阻挡,没有的话我们就计算他的着色值并叠加到成像平面上的eye ray对应的具体像素上即可。
算法里没有细说的点是,光线在传播的过程中难免发生能量的衰减。显然我们总是需要遵守能量守恒定则,所以这些都是需要具体计算的技术问题;当然,技术问题显然也不止这一个,现在我们就要具体地一个一个解决。
Ray-Surface Intersection

光线与表面交互:

我们先这样定义一个光线的方程,r(t)=o+td。其中o就代表光源向量,d就代表光线的单位向量。

接着写出一个球面方程:,应该也不难理解,这样综合两个数学方程就可以得到交点方程:

一顿劈里啪啦的计算后,得到这条光线与球面产生交点需要的时间。

事实上,我们都知道几何有显示表示和隐式表示两种表示方法。隐式几何的表面也是用数学算式来表示的,与之前的推导一样,我们只需要联系光线的方程与隐式表面的方程,就可以求解出所有的表面交点。

显式表面呢?我们都知道所谓的显式几何都是由一个个三角形组成的,所以光线与显式几何的求交点就变成了光线与三角形求交点的问题。
补充一下:这里闫老师有提到我们显式几何的性质可以进行一个点是否在几何体内的判断(要求几何体必须是封闭的),简单地说如果一个点在几何体内,从这个点打出的光线无论方向朝哪,这跟光线与物体的交点都是奇数,反之如果在外部,那么无论方向朝哪,交点都是偶数。

光线与三角形面是否有交点,要如何判断呢?我们将这个问题先放大再缩小,我们先检测这根光线与三角形面所在的平面是否相交,接着再判断这个交点是否在三角形内即可。

现在的问题来到了我们如何定义平面了,这个其实从中学我们就学过,比如说图中经典的点法式:只需要一个法向量和一个点,我们就能确定一个平面。

于是我们又得到求解一条光线与平面的方程,接下来的工作就是判断这个交点是否在三角形内。

这里有一个很简单的算法,简单地说根据我们之前学习的重心坐标,我们将交点在光线上的方程与在三角形内部的方程同时写出。此时所有的三维向量都是已知量,只有t,b1,b2为未知量,三个方程式能求三个未知解,于是我们解出这三个值。然后只需要判断这三个值是否合理即可,比如我们都知道t肯定大于0,同理由之前重心坐标部分的学习可知,我们的重心坐标方程的三个系数也必须是非负数。

现在我们知道了如何求光线与三角形面的交点,可是问题显然还没有结束。具体到一个具体的三维体,一个三维体可能有几百个,几千个甚至几万个三角形,我们难道要每个三角形挨个尝试是否与光线求交吗?显然不对。
Bounding Volumes

第一个方法:包围盒。
简单地说,我们将形状复杂的物体放置在一个包围盒内,如果这个包围盒都没有与光线产生交集,就不用测试这个包围盒内的东西了。

比如:轴对齐包围盒(AABB):简单地说,我们将一个长方体想象成三个对面的交集,然后用这个轴对齐包围盒来进行光线求交。

为什么要提到对面的概念呢?我们把这个求交的过程放在二维平面上就方便理解了,我们分别有两组对线,然后分别求出光线经过这两组对线的两个交点,这样得到两条线段,我们只用求这两段线段的交集即可。

同理的,我们将包围盒的概念上升到三维。我们将AABB视作一个三对对面的交集,然后我们只需要求出光线在这三组对面,也就是六个交点的中的最大的进入时间以及最小的离开时间。这就是我们的在包围框里的光线停留时间。

我们通过得到的离开时间小于0的话说明光线在包围盒背后,也就是没有交集;离开时间大于0而进入时间反而小于0?那就说明你光源就在体内;综上,光线和包围盒有交点的前提是:离开时间大于进入时间且离开时间大于等于0;

关于为什么要使用轴对称包围盒:我们之前推导出的求光线与平面的交点的算式,那么如果是常规的求交点,一个包围盒我们需要求六个交点,可是如果是轴对称包围盒的话,我们只需要分开考虑x,y,z三个分量即可,同时还可以省去求平面法向量的步骤。
虽然我们得到了具体的三角形和光线求交的方法以及包围盒的概念,具体要怎么操作呢?请接着往下看:
Uniform Spatial Partitions(Grids)

均匀网格:

我们来到一个具体的场景,将场景切分成一个个大小均匀的网格,然后将有检测物体(表面)的网格进行标记。

接着我们将光线放入场景中,这时候我们只需要检测一个格子内是否同时有光线与物体表面,然后在这样的格子内进行光线与物体的求交即可。
不难看出核心的思路就是依然利用光线和物体求交的方法,但是不断减小需要检测的范围即可。
但是关于格子,划分格子本身也是一个计算的过程,划分多少格子合适呢?

一个格子——等同于没有加速。

太多格子:要计算太多次光线与格子的相交检测,大大提高了计算量。

综上所述,我们只需要知道格子太多太少的问题即可。划格子这种方法确实有他的用武之地,但是绝非最佳方法。我们设想这样一个场景里,有部分场景物体多而另一部分物体少,为了体现出物体的部分的精细程度我们需要更多的格子,但是对另一部分物体少的场景就造成了计算资源的浪费。
Spatial Partitions

那么这个时候我们就要提出新的方法:空间划分了。

以上是几种比较常见的空间划分方法:
八叉树(Oct-Tree):根据维度,每一维都从中点将空间切成两块。
KD树:根据轴,交替地沿着物体进行切割。
BSP树:与KD树有些类似,但是切的方向不是按着横平竖直的方向来。

KD树的结构:我们将一个空间如图划分成这样(划分到不想再划分为止),接着再将包含物体的最小块信息存储起来即可。对应在数据结构上,就是我们只需要存储叶子节点的值即可。

现在我们将KD树与实际的光线追踪进行一个结合:

首先我们得到一个整体的光线进入时间与光线离开时间。

然后我们开始逐个检查叶子节点,只要是叶子节点的空间与光线有所交集,我们就要对叶子节点的空间内所有物体进行求交。

发现没有交点,我们更新新的进入时间。

接着下一个叶子节点,同样对这个空间进行求交,发现光线与这个空间内的所有物体仍然没有交点。

到下一个叶子节点,这次终于发现有交点了。

空间划分是一种优于直接将空间划分成均匀格子的划分方式,他通过某种有规律的方式来减小需要进行光线求交的检测范围,但是过于僵硬的做法在面对足够复杂的场景依然不实用。比如我们这个图例中与光线产生交点的圆,其实他同时存储在3,4,5三个叶子节点中,这样无疑是对空间资源的一种浪费,也不符合人们的期望。
Bounding Volume Hierarchy

接下来要介绍的就是基于物体来进行划分的方法:Bounding Volume Hierarchy。事实上,这也是目前主流的光线追踪方法。

我们将物体不断分类,并且计算不同类的bounding box,直到每个bounding box 内都包含足够少的三角形即可。

如何具体地来划分物体来形成bounding box呢?有一个思路是我们始终沿着长度最长的轴来划分,另一个思路是我们总是从位置在最中间的物体开始划分。在这里会涉及到一个寻求一系列坐标中的中位数的问题,那么在算法中的快速排序中的快速划分算法思想是可以应用在这里的。

这就是空间划分与物体划分的区别。划分空间就会存在一个物体存在两个空间内,也就是空间内物体重叠;而物体划分会存在一个bounding box内存在空间的重叠。
Radiometry-Motivation

接下来我们要进入辐射度量学的部分了,为什么要学习这部分呢?在之前我们学习着色时,我们对于光的信息往往只是用一个数字简单地表示其强度。而现在我们所学的辐射度量学,就可以更精准地将光的各个信息表示出来。

在这里我们引入比较重要的几个性能指标:
光通量,光强,光辐照度,光辐亮度。

光通量求解算式:单位时间内的光通量等于光的能量除以单位时间。值得一提的是引出了一个新的光通量单位:流明(lumen),代表光的亮度。

光强,由光通量除以立体角即可,可问题在于如何准确地理解立体角呢?

和以前学过的平面角大差不差的是,都是用一个圆(球)来表示,圆中是用弧长除以半径,总和是360度,而球中是用面积除以半径的平方,总和是4Πsr(sr是一个新的单位,立体角,代表一个点出发的锥形区域在单位球上的投影)。
这里提出了一些具体的立体角的推导,都是有关微积分方面的计算。




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


所有评论(0)