计算机图形学基础(二):光线追踪(Ray Tracing)

所有专栏系列的内容均为本人(b站id:宁牁兒)精心总结和排版,仅用于免费学习交流,任何人不得擅自用于商业活动

计算机图形计算的最基本任务之一就是渲染三维物体。模型是用特定语言或者数据结构对于三维物体的描述,包括几何形状、视点、纹理以及照明信息等。渲染就是将三维场景中的模型,按照设定好的环境、灯光、材质及渲染参数,二维投影成数字图像的过程。
从数学角度上来说,渲染就是接收一堆物体的数字集合作为输入,产生出一些像素点的矩阵作为输出。我们需要考虑每个物体对最后输出的每个像素点有什么影响,通常有两种渲染方式:
1. 按物体顺序渲染(object-order rendering)。轮询每个物体对象,考察每个对象影响了哪些像素的值并依次更新这些像素值
2. 按图像顺序渲染(image-order rendering)。轮询每个像素,考察每个像素会被哪些物体影响并计算最终的像素值
上述两种渲染方式能产生相同的结果,对3D渲染来说,光线追踪就是一种image-order rendering的渲染算法。
本章涉及到的数学知识点回顾:
笛卡尔坐标系中,给定两点
和
,
可以表示从点
出发指向点
的向量

基础光线追踪算法
光线追踪器(ray tracer)的基础工作就是每次计算一个像素,考察图像中该像素位置能观测到的物体。以特定的像素位为观测点(下图中眼睛的位置),视线(ray)可能会和多个物体相交(T2,T1),我们只关心那个离观测点最近的物体(T2),因为它后面的物体(T1)会被其遮挡住。一旦找到了这个物体(T2),就使用交点、表面法线和其它信息来进行着色计算(shading computation)来决定最终该像素点的值:

因此,一个基础光线追踪器包括三个部分:
光线生成(计算观测光线)
光线相交计算
着色
用伪代码来描述这一算法过程:
透视
计算机产生以前,使用2D的图像来表示3D的物体场景的方法已经被各路艺术家研究过,如立体绘画、鱼眼镜头、外围摄像机,其中大部分方法都是线性透视:3D的物体投影到一个平面上,保证场景内的直线在图像中呈现的仍然是直线。
最简单的投影方法是平行投影:3D的点沿着投影方向移动到与图像平面相交,从而映射到2D的平面上。最终产生的视图由投影方向和图像平面位置决定:如果图像平面和观测方向垂直,那么这个投影就被称作正交的(orthographic),否则就被称作斜交的(oblique)。但在日常生活经验中,更远处的物体看起来更小,因为我们的眼睛不是从单一方向收集光线的,而是从一个特定的点,这就是透视投影:让投影沿着的线都是经过某一个点(视点),透视投影的视图是通过视点位置和图像平面位置决定的。
计算观测光线
基于正交基的计算方法需要建立一个光线的数学表示,一条光线其实就是一个起点和一个传播方向。一条三维直线,从观测点出发并经过图像平面上一点
可以表示为:
其中向量的方向从点
指向点
,可以理解为,从点
出发,沿着向量
的方向前进,经过
个单位向量长度,到达了点
,由所有符合此表达式的点
所组成的直线,就是所要计算的观测光线,其方向与向量
同向:

可以注意到,,
,并且,如果
,则点
相比点
来说更加接近于观测点;如果
,则点
在眼睛的后方。在OOP编程中,想要将此表达式表示为一个类的函数,大致是:
要计算一条观测光线,最常见的构建正交直角坐标系的方法就是以视点为原点,观测方向是
,垂直观测方向向上是
,另一个基向量则为
,如下图所示:

正交视图
对于正交视图来说,所有光线的方向都是。视图光线应该从点
和向量
和
所在的平面出发,现在还需要的就是图像平面的位置信息。可以用四条边来表示图像平面在
和
方向上的边界:
和
分别表示图像的左右边界,
和
分别表示图像的上下边界,一般情况下会有:
且
:

假设我们现在需要在这个大小为的连续平面上拟合出一张像素大小为
的图像,则垂直方向上的像素点间隔为
,水平方向上的像素点间隔为
,并且像素点处于每一个像素网格的中心。所以,假设某一像素点的索引坐标为
,在
平面上的实际实数域坐标为
,坐标系原点为
,
方向的单位向量分别为
,则有:
给定一个像素点$(i,j)$,现在可以计算观测光线的出发点(O)和光线方向(D)了:
所以,正交视图的观测光线可表示为:
透视视图
明白了正交视图的计算方法后,透视视图的计算方法就简单了,所有的光线拥有相同的起点,即视点。但是对于不同的像素点,光线方向不同,由于最后的图像和视点位置有关,假设视点
到图像平面的距离为
,此时观测光线的出发点(O)和光线方向(D)为:

故透视视图的观测光线可表示为:
光线相交计算
现在已经得到了正交和透视视图的观测光线的计算方法,接下来需要找到光线在的范围内与物体的第一个交点,也就是说,寻找光线在区间
的
处与物体表面的第一个交点。
与球体的相交计算
假设一个球体的中心点为,半径为
,对一个球面上的任意一点
,可表示为:
故,对于观测光线上一点,只要满足上式,就是该光线与球面的交点,将
代入上式:
整理可得,
这个式子是一个关于的一元二次方程,根据求根公式
,可以得到:
如果两个解中更小的解在区间内,那么更小的解是交点,否则更大的解是交点。若两者都不在区间内,则该观测光线在此区间内与该球体没有交点。
与三角形平面的相交计算
假设一个三角形的三个顶点分别为,那三角形内部的任何一点
都可以描述为:
那么观测光线与三角形内部某点相交就可以写成:
转换成三维坐标值的形式就是一个方程组:
再转换成标准的线性系统:
其中均为未知数,求解3*3的线性方程组经典方法是克莱姆法则,推导过程比较复杂在此省略,直接给出解,对于一个方程组:
它的解为:
当然,得到的解应该满足,否则代表该光线在该三角形内部没有交点。
代码表示
在OOP编程中,可以把此求交点的函数放在物体表面类中:
当物体不止一个时,我们需要找到沿着光线距离观测点最近的那个交点,最简单方法是将一组物体本身看作另一个物体: