欢迎光临散文网 会员登陆 & 注册

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

2023-08-03 10:25 作者:宁牁兒  | 我要投稿

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

计算机图形计算的最基本任务之一就是渲染三维物体。模型是用特定语言或者数据结构对于三维物体的描述,包括几何形状、视点、纹理以及照明信息等。渲染就是将三维场景中的模型,按照设定好的环境、灯光、材质及渲染参数,二维投影成数字图像的过程。


从数学角度上来说,渲染就是接收一堆物体的数字集合作为输入,产生出一些像素点的矩阵作为输出。我们需要考虑每个物体对最后输出的每个像素点有什么影响,通常有两种渲染方式:

1. 按物体顺序渲染(object-order rendering)。轮询每个物体对象,考察每个对象影响了哪些像素的值并依次更新这些像素值

2. 按图像顺序渲染(image-order rendering)。轮询每个像素,考察每个像素会被哪些物体影响并计算最终的像素值

上述两种渲染方式能产生相同的结果,对3D渲染来说,光线追踪就是一种image-order rendering的渲染算法。


本章涉及到的数学知识点回顾:

  • 笛卡尔坐标系中,给定两点es(s-e)可以表示从点e出发指向点s的向量

  • 笛卡尔坐标系中,给定两点ab,这两个点的距离的平方可表示为向量(b-a)与自己的点积,即(D_%7Bab%7D)%5E2%3D(b-a)%5Ccdot(b-a)

  •  求解线性方程组的定理,克莱姆法则

基础光线追踪算法

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

图1

因此,一个基础光线追踪器包括三个部分:

  • 光线生成(计算观测光线)

  • 光线相交计算

  • 着色

用伪代码来描述这一算法过程:

透视

计算机产生以前,使用2D的图像来表示3D的物体场景的方法已经被各路艺术家研究过,如立体绘画、鱼眼镜头、外围摄像机,其中大部分方法都是线性透视:3D的物体投影到一个平面上,保证场景内的直线在图像中呈现的仍然是直线。

最简单的投影方法是平行投影:3D的点沿着投影方向移动到与图像平面相交,从而映射到2D的平面上。最终产生的视图由投影方向和图像平面位置决定:如果图像平面和观测方向垂直,那么这个投影就被称作正交的(orthographic),否则就被称作斜交的(oblique)。但在日常生活经验中,更远处的物体看起来更小,因为我们的眼睛不是从单一方向收集光线的,而是从一个特定的点,这就是透视投影:让投影沿着的线都是经过某一个点(视点),透视投影的视图是通过视点位置和图像平面位置决定的。

计算观测光线

基于正交基的计算方法需要建立一个光线的数学表示,一条光线其实就是一个起点和一个传播方向。一条三维直线,从观测点e出发并经过图像平面上一点s可以表示为:

%5Cbegin%7Barray%7D%7Bc%7D%0Ap(t)%20%3D%20e%20%2B%20t(s-e)%0A%5Cend%7Barray%7D

其中向量(s-e)的方向从点e指向点s,可以理解为,从点e出发,沿着向量(s-e)的方向前进,经过t个单位向量长度,到达了点p(t),由所有符合此表达式的点p(t)所组成的直线,就是所要计算的观测光线,其方向与向量(s-e)同向:

图2

可以注意到,p(0)%20%3D%20ep(1)%20%3D%20s,并且,如果0%3Ct1%3Ct2,则点p(t1)相比点p(t2)来说更加接近于观测点;如果t%3C0,则点p(t)在眼睛的后方。在OOP编程中,想要将此表达式表示为一个类的函数,大致是:

要计算一条观测光线,最常见的构建正交直角坐标系的方法就是以视点e为原点,观测方向是-W,垂直观测方向向上是V,另一个基向量则为U,如下图所示:

图3

正交视图

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

图4

假设我们现在需要在这个大小为(r-l)*(t-b)的连续平面上拟合出一张像素大小为n_%7Bx%7D*n_%7By%7D的图像,则垂直方向上的像素点间隔为%5Cfrac%7Bt-b%7D%7Bn_%7By%7D%7D,水平方向上的像素点间隔为%5Cfrac%7Br-l%7D%7Bn_%7Bx%7D%7D,并且像素点处于每一个像素网格的中心。所以,假设某一像素点的索引坐标为(i%2Cj),在UV平面上的实际实数域坐标为(u%2Cv),坐标系原点为eUVW方向的单位向量分别为%5Cvec%7Bu%7D%EF%BC%8C%5Cvec%7Bv%7D%EF%BC%8C%5Cvec%7Bw%7D,则有:

%5Cbegin%7Barray%7D%7Bc%7D%20u%20%3D%20l%2B%5Cfrac%7Br-l%7D%7Bn_%7Bx%7D%7D*(i%2B0.5)%5C%5C%20v%20%3D%20b%2B%5Cfrac%7Bt-b%7D%7Bn_%7By%7D%7D*(j%2B0.5)%20%5Cend%7Barray%7D

给定一个像素点$(i,j)$,现在可以计算观测光线的出发点(O)和光线方向(D)了:

%5Cbegin%7Barray%7D%7Bl%7D%20O%20%3D%20e%2Bu%5Cvec%7Bu%7D%2Bv%5Cvec%7Bv%7D%5C%5C%20D%20%3D%20-%5Cvec%7Bw%7D%20%5Cend%7Barray%7D

所以,正交视图的观测光线可表示为:

%5Cbegin%7Barray%7D%7Bl%7D%20%20p(T)%20%3D%20O%2BT*D%20%5C%5C%20%20%3D%20(e%2Bu%5Cvec%7Bu%7D%2Bv%5Cvec%7Bv%7D)%20%2B%20T*(-%5Cvec%7Bw%7D)%20%5C%5C%20%20%3D%20(e%2B(l%2B%5Cfrac%7Br-l%7D%7Bn_%7Bx%7D%7D*(i%2B0.5))%5Cvec%7Bu%7D%2B(b%2B%5Cfrac%7Bt-b%7D%7Bn_%7By%7D%7D*(j%2B0.5))%5Cvec%7Bv%7D)%20%2B%20T*(-%5Cvec%7Bw%7D)%20%5Cend%7Barray%7D

透视视图

明白了正交视图的计算方法后,透视视图的计算方法就简单了,所有的光线拥有相同的起点,即视点e。但是对于不同的像素点,光线方向不同,由于最后的图像和视点位置有关,假设视点e到图像平面的距离为d,此时观测光线的出发点(O)和光线方向(D)为:

%5Cbegin%7Barray%7D%7Bc%7D%20O%20%3D%20e%5C%5C%20D%20%3D%20-d%5Cvec%7Bw%7D%2Bu%5Cvec%7Bu%7D%2Bv%5Cvec%7Bv%7D%5C%5C%20%5Cend%7Barray%7D%0A

图5

故透视视图的观测光线可表示为:

%5Cbegin%7Barray%7D%7Bl%7D%20%20p(T)%20%3D%20O%2BT*D%20%5C%5C%20%20%3D%20e%2B%20T*(-d%5Cvec%7Bw%7D%2Bu%5Cvec%7Bu%7D%2Bv%5Cvec%7Bv%7D)%20%5C%5C%20%20%3D%20e%2BT*(-d%5Cvec%7Bw%7D%2B(l%2B%5Cfrac%7Br-l%7D%7Bn_%7Bx%7D%7D*(i%2B0.5))%5Cvec%7Bu%7D%2B(b%2B%5Cfrac%7Bt-b%7D%7Bn_%7By%7D%7D*(j%2B0.5))%5Cvec%7Bv%7D)%20%5Cend%7Barray%7D

光线相交计算

现在已经得到了正交和透视视图的观测光线的计算方法,接下来需要找到光线在T%3E0的范围内与物体的第一个交点,也就是说,寻找光线在区间%5BT%7B0%7D%2CT%7B1%7D%5D%2C(T%7B0%7D%3D0%2CT%7B1%7D%3D%2B%5Cinfty)T处与物体表面的第一个交点。

与球体的相交计算

假设一个球体的中心点为C%3D(x_%7Bc%7D%2Cy_%7Bc%7D%2Cz_%7Bc%7D),半径为R,对一个球面上的任意一点P%3D(x%2Cy%2Cz),可表示为:

%5Cbegin%7Balign*%7D%20(P%20-%20C)%5Ccdot(P%20-%20C)%20-%20R%5E2%20%3D%200%5C%5C%E6%88%96%5C%5C%20(x%20%E2%88%92%20x_%7Bc%7D)%5E2%20%2B%20(y%20%E2%88%92%20y_%7Bc%7D)%5E2%20%2B%20(z%20%E2%88%92%20z_%7Bc%7D)%5E2%20%E2%88%92%20R%5E2%20%3D%200%20%5Cend%7Balign*%7D

故,对于观测光线上一点P(T),只要满足上式,就是该光线与球面的交点,将P(T)%3DO%2BT%5Ccdot%20D代入上式:

%5Cbegin%7Balign*%7D%20(O%2BT%5Ccdot%20D-C)%5Ccdot(O%2BT%5Ccdot%20D-C)-R%5E2%3D0%20%5Cend%7Balign*%7D

整理可得,

%5Cbegin%7Balign*%7D%20(D%5Ccdot%20D)T%5E2%2B2D%5Ccdot(O-C)T%2B(O-C)%5Ccdot(O-C)-R%5E2%3D0%20%5Cend%7Balign*%7D

这个式子是一个关于T的一元二次方程,根据求根公式x%20%3D%20%5Cfrac%7B-B%5Cpm%5Csqrt%7BB%5E2-4AC%7D%7D%7B2A%7D,可以得到:

%5Cbegin%7Balign*%7D%20T%20%26%3D%20%5Cfrac%7B-D%5Ccdot(O-C)%5Cpm%5Csqrt%7B(D%5Ccdot(O-C))%5E2-(D%5Ccdot%20D)((O-C)%5Ccdot(O-C)-R%5E2)%7D%7D%7B(D%5Ccdot%20D)%7D%20%5Cend%7Balign*%7D

如果两个解中更小的解在区间%5BT%7B0%7D%2CT%7B1%7D%5D内,那么更小的解是交点,否则更大的解是交点。若两者都不在区间内,则该观测光线在此区间内与该球体没有交点。

与三角形平面的相交计算

假设一个三角形的三个顶点分别为A%2CB%2CC,那三角形内部的任何一点P都可以描述为:

%5Cbegin%7Balign*%7D%20P%20%26%3D%20A%2B%5Cbeta(B-A)%2B%5Cgamma(C-A)%2C%20(%5Cbeta%3E0%2C%5Cgamma%3E0%2C%5Cbeta%2B%5Cgamma%3C1)%20%5Cend%7Balign*%7D

那么观测光线与三角形内部某点相交就可以写成:

%5Cbegin%7Balign*%7D%20P(T)%20%26%3D%20O%2BT*D%20%3D%20A%2B%5Cbeta(B-A)%2B%5Cgamma(C-A)%20%5Cend%7Balign*%7D

转换成三维坐标值的形式就是一个方程组:

%5Cbegin%7Bcases%7D%20x_%7BO%7D%2BTx_%7BD%7D%3Dx_%7BA%7D%2B%5Cbeta(x_%7BB%7D-x_%7BA%7D)%2B%5Cgamma(x_%7BC%7D-x_%7BA%7D)%5C%5C%20y_%7BO%7D%2BTy_%7BD%7D%3Dy_%7BA%7D%2B%5Cbeta(y_%7BB%7D-y_%7BA%7D)%2B%5Cgamma(y_%7BC%7D-y_%7BA%7D)%5C%5C%20z_%7BO%7D%2BTz_%7BD%7D%3Dz_%7BA%7D%2B%5Cbeta(z_%7BB%7D-z_%7BA%7D)%2B%5Cgamma(z_%7BC%7D-z_%7BA%7D)%20%5Cend%7Bcases%7D

再转换成标准的线性系统:

%5Cbegin%7Bbmatrix%7D%20x_%7BA%7D-x_%7BB%7D%20%26%20x_%7BA%7D-x_%7BC%7D%20%26%20x_%7BD%7D%5C%5C%20y_%7BA%7D-y_%7BB%7D%20%26%20y_%7BA%7D-y_%7BC%7D%20%26%20y_%7BD%7D%5C%5C%20z_%7BA%7D-z_%7BB%7D%20%26%20z_%7BA%7D-z_%7BC%7D%20%26%20z_%7BD%7D%20%5Cend%7Bbmatrix%7D%20%5Cbegin%7Bbmatrix%7D%20%5Cbeta%5C%5C%20%5Cgamma%5C%5C%20T%20%5Cend%7Bbmatrix%7D%20%3D%20%5Cbegin%7Bbmatrix%7D%20x_%7BA%7D-x_%7BO%7D%5C%5C%20y_%7BA%7D-y_%7BO%7D%5C%5C%20z_%7BA%7D-z_%7BO%7D%20%5Cend%7Bbmatrix%7D

其中%5Cbeta%2C%5Cgamma%2CT均为未知数,求解3*3的线性方程组经典方法是克莱姆法则,推导过程比较复杂在此省略,直接给出解,对于一个方程组:

%5Cbegin%7Bbmatrix%7D%20a%20%26%20d%20%26%20g%5C%5C%20b%20%26%20e%20%26%20h%5C%5C%20c%20%26%20f%20%26%20i%20%5Cend%7Bbmatrix%7D%20%5Cbegin%7Bbmatrix%7D%20%5Cbeta%5C%5C%20%5Cgamma%5C%5C%20T%20%5Cend%7Bbmatrix%7D%20%3D%20%5Cbegin%7Bbmatrix%7D%20j%5C%5C%20k%5C%5C%20l%20%5Cend%7Bbmatrix%7D

它的解为:

%5Cbegin%7Balign*%7D%20%5Cbeta%20%26%3D%20%5Cfrac%7Bj(ei-hf)%2Bk(gf-di)%2Bl(dh-eg)%7D%7BM%7D%2C%5C%5C%20%5Cgamma%20%26%3D%20%5Cfrac%7Bi(ak-jb)%2Bh(jc-al)%2Bg(bl-kc)%7D%7BM%7D%2C%5C%5C%20T%20%26%3D%20%5Cfrac%7Bf(ak-jb)%2Be(jc-al)%2Bd(bl-kc)%7D%7BM%7D%2C%5C%5C%20%E5%85%B6%E4%B8%AD%20M%20%26%3D%20a(ei-hf)%2Bb(gf-di)%2Bc(dh-eg)%20%5Cend%7Balign*%7D

当然,得到的解应该满足T%7B0%7D%3C%3DT%3C%3DT%7B1%7D%2C%5Cbeta%3E0%2C%5Cgamma%3E0%2C%5Cbeta%2B%5Cgamma%3C1,否则代表该光线在该三角形内部没有交点。

代码表示

在OOP编程中,可以把此求交点的函数放在物体表面类中:

当物体不止一个时,我们需要找到沿着光线距离观测点最近的那个交点,最简单方法是将一组物体本身看作另一个物体:








计算机图形学基础(二):光线追踪(Ray Tracing)的评论 (共 条)

分享到微博请遵守国家法律