Ray Tracing

Why Ray Tracing?

Ray Tracing 主要是解决光栅化中不太好解决的全局效果的问题,我们知道光栅化是只关注Shading Point和光源的效果,Shading Point与其他周围的物体完全无关,这就会很难做到一些全局效果,比如:软阴影,间接光照,Glossy reflection等等,光栅化中光线命中到物体后就不会发生反弹了,但是在光线追踪中,光源会发生多次的弹射。
光栅化的优点是一种很快的方法,但是渲染质量非常的低。 而光线追踪的优点是可以得到更好的质量,但是速度非常慢。

Basic Ray Tracing Algorithm

Light Rays

在光线追踪中,我们首先需要定义光线,我们假设

  • 光线是按直线传播的
  • 光线之间不会发生碰撞
  • 光线是从某个位置发射出来经过场景中各种弹射进入你的眼睛(光线的可逆性:光线从光源出发经过场景到达眼睛,那么你沿着这个光线运动的轨迹能找回发射出它的光源,如果我的相机能看到光,那么沿着这条光路一定能找到光源)

光线追踪大概是这么一个过程:从摄像机(一个点)出发,通过一个显示平面,这个显示平面被分成了许多个像素,每个像素向场景中发射出一条光线(eye ray),如果这个光线与场景中的物体相交了,那么我们就对第一个交点(intersection point)向光源进行连线,如果这个连线没有被其他物体遮挡,那么我们就对这个像素做一个着色。

Whitted-Style Ray Tracing

上面我们描述的光线追踪还是只发生了一次光线弹射,但是在Whitted-Style Ray Tracing中,我们允许光线多次弹射,并且每弹射一次都会对场景中的物体进行着色,它是一种递归光线追踪的概念 Whitted-Style Ray Tracing 的主要思路是:
在我们之前的基础上如果我们的 eye ray 第一次与场景中的物体相交,那么会在这个物体上发生反射和折射(如果可以),如果这个反射和折射的光线继续与场景中的物体相交,那么会继续反射和折射,知道这个光线不会与物体相交。可以看到这明显是个递归的算法,我们也可以定义光线最多弹射的次数,以限制无限递归)(因为有可能一个光线在两个物体之间发生无数次弹射)。 由于我们的光线发生了多次的弹射,我们需要计算多个弹射点的着色的值,我们会把所有的弹射点与光源进行连线,看这个弹射点与光源之间有没有其他的物体,如果没有就对这个弹射点进行着色,最后我们把这些弹射点的着色的值都加权平均加到射出eye ray的像素上

Ray-Surface Interaction

上面在我们的描述中有一个非常常用的操作就是计算光线与场景中的物体的交点。 在数学上,我们将光线定义为有一个起点$\mathbf{o}$的方向向量$\mathbf{d}$,对于这个光线上的任意一个点我们都可以用下面这个式子表示

$$\mathbf{r}(t) = \mathbf{o} + t\mathbf{d} \quad 0\le t < \infty$$

Ray Intersection With Spheres

对于一个光线,我们表示为:

$$\mathbf{r}(t) = \mathbf{o} + t\mathbf{d} \quad 0\le t < \infty$$

对于一个球,我们表示为:

$$\mathbf{p}:(\mathbf{p}-\mathbf{c})^2 -R^2 = 0$$

求它们的交点,其实就是解一个方程组的问题,我们需要求出这个方程组的解,这个解满足光线方程也满足球的方程,所以可以让$\mathbf{r}(t)=\mathbf{p}$,这时我们可以将方程组转化为

$$(\mathbf{o} + t\mathbf{d}-\mathbf{c})^2 -R^2 = 0$$

方程中 $\mathbf{o}$ 、$\mathbf{d}$、$\mathbf{c}$、$R$是已知的,展开我们得到 这是一个二次方程,通过求根公式,我们可以求出$t$,代入光线方程或者球的方程就能得到这个光线与球的交点

Ray Intersection With Implicit Surfaces

对于一个隐式曲面,我们表示为:

$$\mathbf{p}:f(\mathbf{p}) = 0$$

与球类似,我们可以让$\mathbf{r}(t)=\mathbf{p}$,得到

$$f(\mathbf{o} + t\mathbf{d}) = 0$$

然后求解出这个方程的实数范围内的正根$t$,代入光线方程或者隐式曲面方程就能得到这个光线与隐式曲面的交点

Ray Intersection With Triangle Mesh

求三角形和光线的交点实际上就是

  • 求平面与光线的交点
  • 判断这个交点(如果有)是否在三角形内(我们已经会了)

在数学上定义一个平面非常简单,我们只需要通过一个法向量和一个点就能定义一个平面了,法线决定了平面的朝向,点决定了平面的位置。 对于一个平面,我们表示为:

$$\mathbf{p}:(\mathbf{p}-\mathbf{p}^\prime)\cdot\mathbf{N} = 0$$

而我们又可以令$\mathbf{r}(t)=\mathbf{p}$有

$$(\mathbf{o} + t\mathbf{d}-\mathbf{p}^\prime)\cdot\mathbf{N} = 0$$

最终我们得到

$$t = \frac{(\mathbf{o}-\mathbf{p}^\prime)\cdot\mathbf{N}}{\mathbf{d}\cdot\mathbf{N}}\quad 0\le t < \infty$$

当然我们也可以一步到位直接计算光线与三角形面的交点,使用下面的式子就行了

Acclerating Ray-Surface Interaction

我们已经知道了如果计算光线与三角形求交了,但是我们的模型通常有上百万个三角形,如果你的平面是2k的那么计算的时间将会非常爆炸,所以我们需要使用一种方式加速这个过程,当然不使用加速结构,我们已经能进行渲染了,但是会非常慢。 下面是一个有1000w以上的三角形的场景的渲染结果 我们通常使用一种Bounding Box包围盒的方式做加速,包围盒是一种用简单几何体包围模型的一种结构,我们对复杂的模型进行求交非常的困难,但是对一个简单的包围盒求交非常的简单。如果光线和包围盒都没有相交那么更不可能与包围盒内部的模型相交。 下面从左到右是三种不同的保卫盒

  • Axis Aligned Bounding Box:轴对齐包围盒。盒子的各边始终与世界坐标系的 X、Y、Z 轴平行
  • oriented bounding box: 有向包围盒。它不强制与轴平行,而是随着物体的旋转而旋转
  • Sphere bounding box: 包围球。一个完全包裹物体的最小球体。

我们通常使用AABB包围盒也就是一个长方体,在空间中一个长方体可以被定义为被三个不同轴向的对面的交集

所以我们现在的问题就是光线怎么和AABB求交 对于二维的情况,我们可以求出一个光线什么时候与x轴平面相交,什么时候与y轴平面相交,而我们可以通过这两个信息求出什么时候光线在AABB内,也就是图中前两个红线的交集,这个思想是当光线最晚与x,y,z轴平面相交的时候,光线就进入了AABB,只要光线第一次与x,y,z轴平面相交的时候,光线就离开了AABB

我们之前已经知道了光线怎么和平面求交,现在我们看一种特殊情况,即平面是与坐标轴平行的
如果我们的平面与x轴平行那么我们光线与平面求交最后的结果是

$$t = \frac{\mathbf{p}_x^\prime - \mathbf{o}_0}{\mathbf{d}_1}$$

平行其他轴类似