1.求交

光线追踪主要的计算量来源于大量的求交计算。设O代表射线起点,D方向 ,P为圆上的点,C为圆心,r半径。球的方程为:(P - C)(P - C) = r * r ,直线的参数方程: p(t) = O + tD。

将直线方程代入后得D2t2+2(O-C)Dt+(O-C)2-r2=0,随后利用一元二次方程求根公式,判断有无解,有两个解时,选择>0且较小的t。

求交的基本原理就是将射线的参数方程代入到圆的函数中,求t的值。

  1. 将P(t) = O + tD 代入圆方程,会得到 t 的一元二次方程。
  2. 先求出Vec op,op是用球心p的坐标减去射线的起点 (O - C)。
  3. b = op.dot(r.d)指代 ” D * (O - C) ”
  4. 求det,这里要注意我们求的b和原理中的b差了两倍,所以可以直接用
    double det = b b - op.dot(op) + rad rad;
    如果det<0说明无解,直接return 0;
    否则求根号的det;
  5. 最终的解有一个或两个,可在 t = b - det,或者t = b + det中,选择t大于0并且两个中较小的t。

2.绘制

  1. 用6个很大的球体当做平面(DIFF属性,只有漫反射),因为半径很大的话,你在近距离看起来,球面就很像一个平面。

  2. 这样做应该是为了避免去写平面求交,平面类等函数。

  3. 用1个球表示光源,就是Lite,1个Mirr球(完全反射),1个Glass球(折射和反射都有)

  4. 遍历所有的球,求交点

  5. 此光线射出去,在所有的球体中求交点。

  6. 求出距离camera最近的交点,这就是待会要绘制在屏幕上的主要的点。

3.主函数说明

  1. camera的位置是在(50, 52, 295.6), 往z轴的负方向看。

  2. 遍历每个像素点,用随机采样的方式求得要射出的光线的方向d。

4.光线追踪递归说明

_Vector radiance:实现了光线跟踪处理流程,该函数中进行了递归调用。光线跟踪递归过程终止条件是光线与环境中任何物体均不相交,或交于纯漫射面、被跟踪光线返回的光亮度值对像素颜色的贡献很小、已递归到给定深度。该函数传入两个参数,一个是射线的引用,一个是递归的深度;

首先求出射线相交物体的距离以及与射线相交物体的id,如果没有相交,则返回一个emission(0,0,0)的向量。如果相交,求出物体被击中的那个点,并计算法向量normal,normal_real并进行向量单位化。然后判断递归是否达到给定深度,深度大于100就结束。深度大于5时,从0-1随机一个浮点数与RGB颜色分量中的最大值P进行比较,如果随机的数小于P,就返回当前的颜色值。否则就根据球体的材质类型,进行反射折射等计算。其中漫反射取随机数以及w、u、v三个正交向量求出一个随机的漫反射光线,并继续迭代。镜面反射则直接求出反射光的角度。反射加折射首先判断normal和normal_real是否为同一方向,然后计算折射率和入射角余弦,进行菲涅尔折射反射等计算,最后返回颜色值,使用了轮盘赌的算法进行递归调用;

设定好递归出口(depth的值),对每个球体与光线求交,并使得法向量与ray._direct呈钝角(法向量指向球体外。

  1. 判断是否相交,求交点,求表面法向

  2. 漫反射(DIFF)

如果材质是漫反射,那么就随机生成一个方向进行漫反射。

利用法线向量w与向量(0,1,0)或(1,0,0)进行叉乘运算得到向量u,随后w与u进行叉乘得到向量v,利用叉乘运算的方向得到了一组标准正交基w,u,v。利用随机函数drand48()得到两个随机数r1,r2,通过二者的运算得到3个坐标,进而得到在标准正交基w,u,v下的一个随机向量direct,即求得了一个随机的漫反射光线从而继续递归。

  1. 镜面反射(材质为SPEC)

计算镜面反射的方向,然后继续递归

由于漫反射和镜面反射都遵循反射规律,因此根据反射定律计算出反射光的方向,进而继续递归。

  1. 反射和折射(材质为REFR)

玻璃材质,有一部分光进行反射,有一部分光进行折射。

这里用到了轮盘赌方法。

首先,计算出相对折射率,由公式n1sinn1 = n2 sinn2可以计算出折射角的正弦值,同时根据入射光线的方向,法线方向以及折射的角度可以计算出折射方向从而生成折射光线;根据菲涅尔近似等式,可计算出菲涅尔反射和折射所占的比例(Fr+Fe = 1),从而继续递归。

Ray reflRay(x, r.d - n 2 n.dot(r.d)); // Ideal dielectric REFRACTION 由平行四边形的方法求得反射光的direction