Raytrace学习的阶段性总结
Raytrace学习的阶段性总结
最近一个月一直在学习RAYTRACE,从理论到代码实现,之前最近才终于有了一个比较完整的版本,遂打算把之前的一些学习经历写出来。
RAYTRACE又称光线跟踪,业界公认此算法为Turner Whitted在1980年提出,而它本身又是由原来的光线投射算法(ray casting)演变而来的,ray casting本身是用来消除场景中隐藏面的一种算法,Whitted将其扩展为递归算法后则成为了传统的raytrace算法。标准的raytrace一般采用逆向跟踪技术来完成整个绘制,即从视点出发计算屏幕上每一个像素点的颜色值。其大部分过程是于物体求交,若该光线击中物体,那么要先判断物体的材质,若为理想漫发射表面,则利用局部光照模型(因为可以方便地对顶点法向量进行插值,所以可以方便地使用phong着色模型)求出一个颜色值,若为镜面反射值或透射值,则产生一道反射(透射)光线,递归地计算,这也就是说假如当前场景所有物体都是理想漫反射材质的话,raytrace算法也就退化成了raycasting。当然实际实现的时候肯定没有上面说的这么简单,由于有太多细节要考虑,就简单说说实现的历程和效果吧。
1阶段.只有primary ray(raycasting)
对每个像素点进行投射求交,然后利用局部光照模型(phong)计算颜色值,并有阴影点判断(阴影点判断本身也是一个raycasting,即从该点到光源作一次光线投射,检测其中有没有障碍物)。
(图1)
2阶段.加入了reflection ray,这样就可以处理镜面,可以这样理解这个问题,当人的视线击中一个镜面的时候,那他还可以看见视线(可以想象为一条射线)由镜面发射之后击中的物体表面,这其中就多了一个求反射向量,和颜色叠加的过程。
(图2)
3阶段.加入了refraction ray,这样可以处理透明物体,当然光的折射满足折射定律,并且需要处理全反射和透射衰减的情况,并且一般的透明材质的内外表面都还有反射(这个地方感觉自己一直都处理得不好),具体的细节以后有时间的话再给出吧。
(图3)
4阶段.加入了简单的纹理映射,只针对球面和平面(因为目前这个阶段也只做了球,轴对齐矩形和轴平面这几个物体,还没有加入三角形)。而球面映射,我采用的也是最简单的经纬坐标映射。这个部分同时需要加载图像文件,由于时间有限,我自己只写了一个24位BITMAP的加载以及直接抄袭了Jacco Bikker的一段TGA加载的代码(实际上我从头到尾都参考了他的代码)。
(图4)
5阶段.加入了三角形的处理,加载OBJ文件模块,使用KD-TREE对场景进行优化,以及各种其他优化,不过效率仍然很低,特别是打开全屏抗锯齿以后。(还是感觉效果不是很好啊!)
(图5)
关于优化的一些心得:
1.stl在debug版本和release版本下的实现貌似不同,在debug版本下hash_map的find操作非常耗时,但是到了release版本竟然快如闪电,还有vector的一些操作也是这样,让我白白花费了好多时间来尝试对其进行优化。
2.对于文件的读入,特别是一些大的模型.obj文件或texture通常都是几兆甚至数十兆,应该尽量一次性全部读入内存(最多分几次读入)然后再进行分析,加载模型这一个函数就够你受的。
3.在关键代码处最好不要用运算符重载。运算符重载虽然可以使代码的可读性大幅提高,但是在执行频率很高的代码段还是别用的比较好,记得有一次就是只改了一个求交函数中的运算符重载代码,结果计算时间直接从8s减至7s。
不过即使这样效率仍然较低,没有用SSE优化,KD-TREE里的SAH划分子空间标准也没有完全实现,勉强能应付三角形面在10W以下的场景。
后记:接下来可能会打算将RAYTRACE移植到GPU上,利用GPU强大的并行计算能力来加速。同时开始学习GI。争取以后再实现一些multipass的渲染算法,如photon mapping。还有太多的东西要学习,努力!