[图形学] 光线追踪中的数学方法

来源:互联网 发布:ct数据处理软件 编辑:程序博客网 时间:2024/05/02 00:47

reference: 《Mathematics for 3D Game Programming and Computer Graphics》


目录

6.1 求根
    6.1.1 二次多项式
    6.1.2 三次多项式
    6.1.3 四次多项式
    6.1.4 牛顿法
    6.1.5 倒数及平方根的优化
6.2 表面相交
    6.2.1 光线与三角形的相交
    6.2.2 光线与立方体的相交
    6.2.3 光线与球体的相交
    6.2.4 光线与圆柱体的相交
    6.2.5 光线与圆环体的相交
6.3 法线计算
6.4 反射和折线向量
    6.4.1 反射向量计算
    6.4.2 折射向量计算

        光线追踪这一术语是指跟随光束来决定它们和世界中的哪个物体相交的算法。
        它的应用包括光照纹理的生成、判断可见性、碰撞检测,以及视线检测。这一章描述了当光线与物体相交时如何找到它们的交点,以及当光线与反射或者折射表面相交时,如何改变光线的路径。

1.求根


        为了找到方程为P(t) = S + tV的直线与表面相交的交点,我们需要求得关于t的n次多项式的根。对于平面而言,这个多项式的次数是1,它的解很好求。对于二次曲面,例如球面或者圆柱面,多项式的次数是2,我们可以求解二次方程来找到一个解。而对于更加复杂的曲面,例如样条曲面和圆环面,多项式的次数达到了3或4,在这种情况下,我们仍然可以分析找到解,但是这将花费更多的计算代价。

        二次,三次以及四次多项式的解析解将在这个部分中介绍。不过,三次以及四次方程解的完整推导已经超越了这本书的范围。我们同样也会验证一个数值求根技术:牛顿迭代法。


        1.1 二次多项式


        我们可以使用一些代数运算来求的t的二次多项式的根。
        

        两边同时减去c,并且同除a:

        

        通过同时加上b^2/(4*a^2),我们可以得到左式的一个完全平方

        

        把左式写成平方的形式,并把右式通分

        

        开方并同减b/(2*a)

        

        这就是著名的二次求根公式。D = b^2 - 4*a*c这个量被称为多项式的判别式,它反映了这个多项式有多少个实根。
        如果D>0,那么它有两个实根,如果D=0,那么它有一个实根,它的值为t=-b/(2*a)。剩下的一种情况是D<0,这时候它没有实根。
        计算判别式可以让我们在不求出具体解的情况下,判断光线是否与物体相交。


      1.2 三次多项式


        形式为

        

        的三次多项式(我们使用了一些必要的除法来保证首项系数为1)可以通过如下的参数替换,转换为一个没有二次系数的式子。

        

        转换后我们得到:

        

        其中:

        

        一旦找到了上式x的解,我们可以减去a/3来得到原式的t。

        三次方程的判别式为

        

        我们设:

        

        现在我们可以这样表达等式的三个复数根:

        

        其中p是1的一个立方根,(注意

  

        我们可以使用换元来极大地简化我们的式子

        

        现在判别式是:

        

        设

        

        我们现在可以这样表达r和s:

        

        和二次方程一样,判别式告诉了我们实根的存在性。如果D‘<0, 方程给出的x1的值表示的是方程的唯一实根。

        如果D‘ = 0,我们有r = s,所以现在存在两个实数根,包括一个双重根:

        

        剩下一种情况D‘>0中,方程有三个不同的根。不幸的是,我们仍然需要利用复数来计算这些解。此外还有另一种不需要复数运算的方法,这个方法主要依赖于以下这个三角等式:

        

        我们可以用欧拉公式来证明这一方程的正确性。对于

        

        我们做一个换元,其中,可以得到:

   

        (注意:为了保证D‘为正,p必须为负)用-3m^2代替p,并把公因数2m^3拿到外面:

        

        代入前面提到的三角等式,并且计算,我们得到:

        

       由于D‘>0,根据

        


        推断出因而保证了等式的右侧的绝对值总是小于1。我们用余弦函数的反函数来表达theta:

        

        所以,我们可以得到原方程(参数为x)的一个解:

        

        由于对任意整数k,都有,我们把之前的式子写成:

        

       通过选择k余3的三个不同的值0,1,2,可以得到不同的值。令k = +-1,我们可以这样表达剩下两个解:

        

        1.3 四次多项式

         

        一个四个多项式有如下的形式:

        

        (同样地,我们做一些必要的除法来保证首项系数为1)可以通过换元转换为没有三次项的式子:

        

        这将原式转换为:

        

        其中:

       

        一旦我们找到了方程的一个解x,我们减去a/4,就能得到原式的解t。

        要求出四次方程的根,首先要解出以下三次方程的根:

        

         令y为这个方程的任一实数根。如果q>=0,那么四次方程的解等价于这两个二次方程的解:

       

        如果q<0,那么四次方程的解等价于这两个二次方程的解:

        


        1.4 牛顿迭代法


        牛顿迭代法,通常简称为牛顿法,是一种通过迭代一个与函数及其导数相关的式子,找到任意连续函数的根的数值分析技术。

        

        (函数的切线与x轴的交点逼近函数的根)


        假设我们想要找到上图中函数f的根。现在,让我们假设我们有一个关于函数根的最初猜测值x0(主要关注的是如何快速选择合适的值)。过点(x0,f(x0))的切线的斜率是由导数f'(x0)来刻画的。我们可以把这条切线用下面的式子来表示:

        

        注意到,这条切线与x轴的交点相比起我们初始的猜测值x0更接近与真实的解。当y = 0的时候,我们可以得到x的迭代式:

        

        在这里,我们用xi来代替x0, 用xi+1来代替x。我们反复代入这个式子,计算得到一个序列x0,x1,x2……,它们的值在正确条件下将不断逼近 f 的根。

        牛顿法能相当快地收敛于根,因而达到一定精确度时需要的迭代次数非常少。我们事实上可以显示出牛顿法是二次收敛的,这就意味着每一次迭代后,近似根的有效数字个数成倍增长。为了证明这一点,我们设:

        

        令r为函数 f 实际的根,我们定义为第i个近似值xi与根r之间的误差:

        

        我们把这个式子代入x的迭代式,得到:

        

        我们可以用泰勒公式的前三项来近似估计g(xi)的值:

       

        g(x)的一次二次导数可以写为:

        

        由于f(r) = 0,当x = r时,这个表达式可以极大的简化,这时函数g以及它的一次二次导数为:

        

        我们把这些带入到前面的泰勒展开式,得到:

        

        最终,我们把这个代入,得到

        

        牛顿法并不保证收敛到解。特别地,如果初始的猜想选择了某一点,使得函数的导数为0,那么切线将是水平的,不会和x轴相交,我们无法进行执行下去。初始的猜想必须在某种程度上接近实际解来保证收敛。当我们寻找一条线与复杂物体的交点时,我们通常能够通过求这条线与物体的简单包围体的交点,来找到一个好的初始点。例如,如果我们想要找到P(t) = S + tV这条光线与圆环的交点,我们首先可以求这条光线与包围着圆环的立方包围盒的交点,然后再利用这个值作为我们求与圆环交点的初始猜想值。


        1.5 倒数和平方根的优化


        绝大多数的现代CPU,包括绝大多数的图形硬件,都可以近似一个数的倒数以及倒数平方根到一定的精确度。硬件提供的计算结果可以利用牛顿法得到更高的精确度。

        数字r的倒数可以通过求以下这个函数的根得到:

        

        因为f(1/r) = 0。将这个式子代入前面的迭代式,我们得到:

        

        这个式子可以被反复迭代以得到数字r的高精度倒数,如果每个xi都大于0的话。这取决于这样一个事实:函数f(x)在x = 0时会达到一个奇点。把这个条件施加到x1上,我们可以确定初始的x0一定会处在的区间是什么。因为x1一定大于0,我们就能得到:

       

        这样就计算出了x0的取值范围:

       

        因此,初始的近似不会比2/r更差。

        r的平方根的倒数可以通过计算如下这个方程的根得到:

        

        同样代入迭代式,我们得到:

       

        只要每个xi>0,这个序列都是收敛的,所以我们的初始解必然满足:

        

        一旦我们计算出了可接受精度的倒数平方根,r的平方根能通过单一的乘法计算得到,因为


2.表面相交


        计算一条光线与一个面的交点是光线追踪的核心。

        我们这样定义光线:

        

        其中S代表光线的起始位置,V代表光线指向的方向。在这一章节中,我们使用常见的物体来讲述光线相交的特定解(其余的物体留作练习)。除了三角形外,相交都是在物体空间计算的,在这个空间里物体的中心和坐标原点重合,它的轴线沿着坐标轴线展开。要完成与任意朝向的物体相交的计算,首先要把光线转换到物体空间。一旦检测到了相交,相关的信息如交点以及该点的法线向量又被转换回世界空间。


        2.1 光线与三角形的相交


        一个三角形面片由它三个顶点P0,P1,P2的位置来描述。我们可以通过计算以下法线来确定这个三角形所在的平面:

        

        到原点有符号的距离d,是由向量N与平面任意点的点乘的相反数计算得到的。所以我们选择顶点P0来构建一个4D平面向量L={N,-N . P0}。正如我们在前面的章节提到的,光线与面L交点关联的t的值为:

        

        如果L.V = 0,那么就不会相交。否则,我们将t反代到光线方程中,得到光线与三角形平面的交点P。

        我们现在面临着这样一个问题,我们需要判断这个点P是否在三角形边的内部。我们通过计算P关于三角形顶点p1,p2,p3的质心坐标来判断。质心坐标表达了三角形顶点的权值平均数,它们用w0,w1,w1来表示:

       

        其中w0+w1+w2 = 1.我们用1-w1-w2来替代w0,得到以下式子

        

        我们定义与p0相关的式子:

        

        把原来的表达式简化为:

        

        等式两边同时进行点乘,首先是和Q1,然后再是Q2,我们得到这样两个式子:

        

        如果把它们写成矩阵形式,是这样的:

        

        我们可以按如下方式轻松地求出w1和w2:

        

        点R在三角形内部当且仅当三个权值w1,w2,w3都是非负的。由于w0 = 1- w1 - w2,这就意味着w1 + w2 <=1.

        如果顶点p0,p1,p2有任何相关的属性,如颜色或者坐标集合,那么我们都可以利用w0,w1,w2通过插值计算得到点R的这一属性。例如,如果P0,P1,P2的纹理坐标分别为<s0,t0>,<s1,t1>和<s2,t2>,那么R点的纹理坐标<s,t>可以表达为:

        

        2.2 光线与立方体的相交

        一个立方盒是由以下六个平面方程描述的:

       

        其中,rx,ry和rz表达了这个盒子所处的范围。在计算光线的交点时,我们最多需要考虑三个这样的平面,因为至少有三个平面是背对光线的朝向V的。我们可以逐个检查V的每个分量来决定我们要计算哪些平面。例如,如果Vx = 0,那么我们就知道这条光线无法与平面x=0以及平面x=rx相交,因为V和它们是平行的。如果Vx > 0,那么我们不需要检测和平面x=rx的相交,因为它相对于光线视角来说,是处在背面的。类似的,如果Vx<0,我们不需要检测和平面x=0的相交。同样的规则也适用于V的y和z分量。

        我们一旦找到了光线与平面的交点,就必须通过检查和平面平行的两个方向对应的坐标,来检查这个点是否落在立方盒的面上。例如,t的值对应着光线与平面x=rx的交点:

        

       为了保证落在盒子的相应面内,点P(t)的y和z坐标需要满足:

      

        如果任一条件不成立,那么就不存在和这个面的交点。如果两个条件都成立,那么就一定能找到一个相交,这时候,我们就不需要测试其它任何平面了,因为不会再发生比这个更好的相交了。(no closer intersection can occur)


        2.3 光线与球体的相交


        一个与球心在原点,半径为r的球体可以由以下方程表示:

        

        我们用光线P(t)的方程来代替x,y,z,得到:

       

        展开平方,并且以t为主元合并同类项,得到以下方程:

        

        我们可以用以向量S,V表示的a,b,c来表达方程的系数:

        

        计算判别式D = b^2 - 4ac 能够告诉我们光线是否与球体相交。正如下图所显示的,如果D<0, 那么不会发生相交;如果D = 0,那么光线与球体相切;如果D > 0,那么存在两个不同的交点。如果光线和球体有两个交点,那么离光线原点S 更接近的交点(对应着t值中更小的那一个),通常写作:

        

        因为我们可以保证a是正数。

        

        光线与椭球体的相交可以把原来的球体方程替换成以下这个方程来计算:

        

        其中m是x半轴长度与y半轴长度的比例系数,n是x半轴长度与z半轴长度的比例系数。我们把光线方程的分量代入这个方程,可以得到另一个二次多项式,它的系数为:

        

        类似的,我们用判别式来判断相交是否发生。如果发生了,就能得到相交的参数t。


        2.4 光线与圆柱体的相交


        椭圆柱体侧表面x轴方向的半径为r,y轴方向的半径为s,高度为h,中心落在x,y平面的原点。它可以由以下方程描述:
        

        

        其中m = r/s。如果r = s,那么这个圆柱体就是圆形的,并且m=1.我们用光线方程P(t)的分量替换原方程中的x,y,得到

        

        展开平方,并以t为主元合并同类项,得到以下这个二次方程:

        

        和球体一样,这个判别式指出了相交是否会发生。这个方程的解给了我们光线和这个在z轴上无限延展的圆柱面的交点值,我们必须检测交点的z轴分量是否满足0<= z < = h。

        在碰撞检测这一章节,问题在于我们需要知道一个移动的球体是否与表达多面体模型的一条线段相交。这个问题可以被转换为一条光线与给定半径和任意端点的圆柱体是否相交。这一内容在第12章中介绍。


        2.5 光线与圆环的相交


        圆环的横截面有一个主半径r1和一个次半径r2,如下图。半径为r1的圆处在x,y平面上;半径为r2的圆的圆心落在前一个圆上,并且与它垂直,它是绕着z轴旋转分布的。这个旋转的圆可以用这样的等式来表达:

        

        

        其中s的值表达了到x,y平面上的大圆的距离:

        

        我们把这个式子代入前面的表达式,得到:

       

        我们把根式分离,然后再平方,得到圆环体的式子:

       

        把光线方程P(t)的x,y,z分量代入这个式子,得到:

        

        适当的代数简化后,我们可以用这个四次方程来表达:

       

        其中

        

        同除a使得首项系数化为1,这个方程可以用我们前面提到的四次方程求解方法解决。如果向量V是标准化的,那么就没有必要除以a,b和c的计算可以被简化为:

        

3.法线计算

        有时候用隐函数f(x,y,z)可以更方便的表达一个表面,所有在表面上的点(x,y,z)在函数中的值都为0,所有不在表面上的点的值都不是0。例如,对于椭圆而言,这个函数是这样的:

        

        使用隐函数表达,我们可以得到一个求表面顶点法线方向的通用计算公式。

        设f(x,y,z)表达一个表面S,所以f(x,y,z) = 0就指代表面上所有的点。我们令C为表面上的任一曲线,它由参数方程x(t),y(t)以及z(t)刻画。那么,曲线C在点(x(t),y(t),z(t))的切向量T可以表达为:

         

        由于C是表面S上的曲线,T也是表面S的切线。同样,由于f(x(t),y(t),z(t)) = 0 对于任意t都成立,我们知道在曲线C上,总有df/dt = 0.使用链式规则,我们可以写成:

        

        因为它与T的点乘也是0,向量一定是表面S的法线。这个向量也叫作f在点(x,y,z)的梯度,它也可以写作其中,倒三角运算符(delta)的定义为:

        

        继续讨论上面的方程,对于一个椭球面而言,我们的法线形式为:

        


4.反射和折射向量 


        当一束光与物体的表面相撞时,它的部分能量被物体表面吸收,部分能量从表面反射回去,部分能量穿过了物体自身。下一章将会详细讲述这些影响。这一章将着重解释当光线和一个光泽的或者透明的表面相交时,反射以及折射的方向。

        4.1 反射向量计算


        光照射到反光面(如镜子)的反射方向遵循这样一个简单的规则:入射角等于反射角。正如下图所显示的,这也就等价于,法线和入射光方向L的夹角等于法线N与反射光方向R的夹角。
        我们假设向量NL都已经标准化到单位长度了。为了得到一个用入射方向L和法线N表达的反射方向公式,我们首先计算出L在与法线N垂直方向的分量:
        
       向量R处在向量L与其在法线上的投影的距离的两倍处,我们可以这样表达R
        
        

        4.2 折射向量计算


        透明表面有一个性质叫做折射率。根据斯涅尔定律,入射角和折射角的关系由以下方程表示:
        
        其中是光线将离开的材料的折射率,是光线即将进入的材料的折射率。空气的折射率通常为1。更高的折射率就会在两个材料的交界处产生更强的弯曲效果。
        我们假设法线向量N以及入射光线L都已经标准化到单位长度。我们用法线的垂直和平行分量来表达透射光的方向T,正如下图显示的,T关于法线的平行分量表达为,垂直分量表达为,其中向量G是与平行的单位向量。由于L是单位长度的,,所以:
        
        

        现在,我们可以这样表示折射向量T
        

        代入折射公式,我们可以替换sin:
        

        我们用代替,然后再次使用折射公式代替sin,我们得到:
        

        用代替,最终得到:
        

        如果,那么根号里的式子可能是负的,这种情况发生在光线从高折射率媒介到低折射率媒介产生宽入射角时。特别地,方程仅在时成立。如果根号里的式子是负的,那么说明发生了全反射。这意味着,光线没有折射,而是发生了反射。
        

0 0