计算机图形学十二:光线追踪原理详解及其加速方法

时间:2024-04-04 09:20:00

Whitted-style光线追踪及其加速方法

摘要

本篇内容主要分为两部分,第一部分介绍最基础的Whitted-style光线追踪的原理,该方法是业界后来许许多多的光线追踪方法变体的基础框架,相对重要。第二部分则会具体介绍一些加速光线追踪的方法,因为众所周知,属于全局光照模型的光线追踪获得了相比局部光照Blinn-Phong模型更好的效果,代价自然是速度很慢,难以做到实时(比如说30FPS以上),所以如何加速光线追踪也是重要的研究问题之一。

1 Whitted-Style 光线追踪

在进入原理讲解之前,我们首先考虑一下为什么需要光线追踪呢?
因为Blinn-Phong这种局部模型无法处理全局效果!
举个例子:
计算机图形学十二:光线追踪原理详解及其加速方法
如上图中房屋顶部的所接受到的光可不仅仅是Blinn-Phong模型考虑的直接光源,还有可能是来自窗外的光源照射到地板,再发生反射照射到了房屋顶部,而这部分光是局部光照模型没有考虑到的,而光线追踪正是为了解决这种问题所提出的一种全局光照模型。
换个角度来考虑,光栅化虽然快但是得到的图像质量一般较低,为了追求更好的结果当然就会寻求更加完美的模型了。

1.1 原理详解

光线追踪听名字就知道,讨论的核心是光线,因此我们首先对光线做一些假设。
1 光线一定沿着直线传播
2 光线之间无法碰撞
3 光线路径可逆,即从A发出的到B的光线,一定也可以从B发出到A(中途可发生反射和折射)

明白了光线的一些假设之后,想一想人为什么能看到不同的物体?是因为从物体表面上有光进入了人眼。那么能不能逆向思考一下,是不是也可理解为人眼发出了很多感知光线碰撞到了物体,所以可以看见呢?在古代可还真就有不少人这么想:
计算机图形学十二:光线追踪原理详解及其加速方法
当然,现代物理知识已经告诉我们这种观点是错误的,但是并不妨碍从中获取一些灵感,考虑一下对光线的第三条假设:光路可逆,所有进入到人眼的光,都可从人眼发出光按照原路反方向返回,那么利用这种模拟从人眼发射光线的方法不就可以还原出所有的光路了呢?没错这就是光线追踪的核心想法,正着不行那就反着来!

第一步 Ray Casting

从人眼或摄像机向近投影平面上的每一个像素点发射一条光线,判断与场景物体的交点,示意图如下:
计算机图形学十二:光线追踪原理详解及其加速方法
当然一条光线自然可能会与不止一个物体相交,但是考虑遮挡关系,只去找最近的交点。接着连接该交点和光源,只要判断这条连线之间是否有物体存在就可以知道该交点是否在阴影之中(怎么样,是不是比shadow mapping那一套简单了许多):
计算机图形学十二:光线追踪原理详解及其加速方法
紧接着,自然可以利用Blinn-Phong模型对这个点进行局部光照模型计算,得到该像素的颜色,那么遍历所有近投影平面上的像素就能得到一张完整的图像。但如果光线追踪仅仅是在第一步Ray Casting就停止的话,那么它的效果与局部光照模型是一样的,因此我们需要第二步,真正的考虑全局效果

第二步 Recursive (Whitted-Style) Ray Tracing
考虑第一步中所做的Ray Casting,该条光线第一个与圆球物体相交,假设该圆球是一个玻璃球,那么便会发生镜面反射,如图:
计算机图形学十二:光线追踪原理详解及其加速方法
当然除了镜面反射之外,自然也存在折射,同时反射与折射出去的光线会可能与场景中的物体再次碰撞,发生第二次折射与反射:
计算机图形学十二:光线追踪原理详解及其加速方法
(为了图示清晰,图中仅以两次折射或反射的部分光线为例)
从图中可以见到,不仅仅是与圆球相交的那一点可以贡献光到达眼睛,折射与反射之后再与物体相交的点也可以贡献光(光路可逆原理)。简而言之,除了直接从光源照射到圆球交点再沿着 eye rays(第一条发射的光线)到眼睛中,也可能存在这样一种情形,有光照射到其他物体,再沿着eye rays的反射或折射的光线方向传回人眼!

因此每一个交点的颜色贡献来自这样种几类型 直接光照,反射方向间接光,折射方向间接光(如果有折射的话)

下一步将这些所有交点与光源连接,称这些线为shadow rays(因为可以用来检测阴影),计算这些所有点的局部光照模型的结果,将其按照光线能量权重累加,最终得到近投影平面上该像素点的颜色!而这就是一个全局光照模型了,因为不仅仅考虑了直接光源的贡献,还考虑各种折射与反射光线的贡献。

以上就是光线追踪的整个过程了,还有额外几点要注意的
tips:1. 整体过程是一个递归的过程,因此需要一定的递归终止条件,比如说允许的最大反射或折射次数为10。
2. 光线在每次反射和折射之后都是由能量损耗的,由系数决定,因此越往后的折射和反射光贡献的能量越小,这也是为什么在上文中提到根据光线能量权重求和。
3. 有一些关于光线表示,及如何求交点的实现细节在1.2节里讨论。

如果读者是第一次接触光线追踪的话,理解起来还是有点难度(包括我第一次也想了挺久),但是只要真正的去实现一遍,就能很快的理解其原理,因此我在这里推荐一个我看过的很不错的教程:RayTracingInOneWeekend 这个教程手把手的教你用c++实现了一个没有带光源的光线追踪器出来,并且还带有许多原理知识的讲解,写的是通俗易懂,相当好!如果没有看过真的可以看看。

先码到这。得干一干导师给的活,不然组会要被喷,过几天有空补上