问就是很闲,0.5倍速一边看一边打的,不是完全一模一样,根据我的手速尽力还原了。方便日后复习定位。
一万三千多个字实在太多了,懒得做二次整理了。。。会看到有些内容比较混乱,毕竟是说话内容直接打出来。建议配合视频使用。
下面这4个块,我们已经说了:光栅化,几何,以及今天讲的光线追踪。Whitted-style光线追踪并不是现在最现代化的解决方法,
光线追踪和光栅化是两个不同的成像方式,那么我们在学习的过程中体会一下有什么区别。
首先我们既然要说光线追踪,那么要有个理由,为什么要引入光线追踪,在引入光栅化的时候,一定是有些问题解决的并不好,我们应该想点办法解决问题。光栅化最大的问题就是说他不是很好去表示一些全局的效果,就是所谓global effects。所谓全局的效果简单列举几个(图上3个)。咱们上面提到说如何用这个shadow mapping的方法去做阴影。咱们也提到假设光源,点光源只能做硬阴影,后面改进了下能够把它给能够支持软阴影,像图一现象,很明显软阴影,离物体越远越模糊。阴影当然是一种全局的效果,我们折腾半天用两趟不同的渲染模式,然后解决了点光源形成的硬阴影问题。所以说光栅化做这个阴影还是挺困难的。那么第二个问题是什么呢,就是这种(图二)glossy反射,物体表面有点像镜子但是又没有镜子那么光滑,这种材质我们管他叫glossy。Glossy有那么些高光,有反射效果,但是本身又有粗糙性,这种情况就是glossy反射。这种反射打到一个物体势必要打到另一个物体,再打到人的眼睛。这种情况下,光线在场景中弹了2次,第一次打到一个物体,然后反射到另一个物体上又是另一个点,然后再打到人的眼睛,包括第三个这个问题叫间接光照(图三),什么是间接光照呢,大家看光源来自大的窗户,光线照到地板,然后经过漫反射,反射到某一个位置上去,有可能反射到一堵墙又发生漫反射,不断弹射最后才打到人的眼睛。从这个角度来说间接光照对他的正式的定义之后再说。现在理解,光线到达人眼之前弹射不止一次,叫做间接光照。这个要怎么要做出来?比如说光栅化,把物体投影到屏幕上任何一个点,然后我做一个所谓着色,这肯定是考虑光线只弹射一次,从光源到着色点到人的眼睛。我们之前还做过Bulinn-Phong模型,像这种情况,当光线弹射很多次,光栅化就非常低效,很难做出效果,不是说不行,当前发展出了很多方法使得光栅化可以支持这种效果,但是一是比较麻烦,二是不能保证正确性。光栅化极限说到这。
我们为什么用光线追踪,我们提到这光栅化有些问题,光栅化通常情况下,我们不说具体问题,大家会认为光栅化是一种很快的,很近似的渲染方法。大家看到这个车子,对于目前的这种渲染来说,首先一看没有阴影,可能画质低,一个是模型细节低,纹理看不清,大家一看就是一个很原始的一种渲染结果,只能做到这种效果吗,是地图非常大,整个渲染对gpu的要求非常高,所以画质上不能做更多要求。所以光栅化本质上来说是快速近似,质量相对较低这个意思。
我们要提到的是光线追踪,是一种准确的方法,但是也有问题的,不是说能替代光栅化的做法,他最严重的问题是非常慢。第一节课提过这个问题,光栅化通常瞄准实时应用来的,所谓实时,每秒钟生成30帧的图。那这个光线追踪通常更多被用来做一些离线的应用,叫offline的应用,也就是说大家看的电影啊。电影商会花大量时间生成这个过程,这个过程非常慢,有多慢呢,1帧就需要花1万个CPU小时,多核的话降低点的话,多核的话花1000CPU小时,生成1张图,那对比一下光栅化这个速度,显然无法比的。所以说质量和速度通常是一个所谓trade off,就是通常图形学里面接触过很多次的,咱们做MSAA对吧,提升质量但是让结果变慢。图形学上有很多和难的问题,光线追踪和光栅化的比较也是如此。这点为什么要提前说呢,他是一种比较慢的方法,做作业的是后会体验到,另外一点,是一种质量非常高的方法,完完全全符合物理规律,这就为什么要学习。我们从最基本的图形学算法开始。
是这么回事儿,在定义光线追踪之前怎么做,先把光线定义下来,我们追踪的是光线,光线是什么呢,这里有简单的3个想法,相当于是我们在图形学中基本就是这么假设的。
首先光线是沿着直线传播的,这个按说是没有问题,其实严格意义来说光线其实是一种光波,有波动性,在一定程度上考虑波动性质,但是对课来说,光线是沿直线传播没问题。第二点呢,光线和光线不会发生碰撞,两个光线X形光线过来,发生交叉,不耽误两个光线各传播各的,当然这是不对的,我们只是这样假设。然后呢这点很重要,光线呢肯定是会从光源被发出来打到场景中再在场景中反射折射等操作,最终进入眼睛。我们不止一次提为什么我们可以看到图像,是因为光线到达我们眼睛。那么整个光线追踪就在试图模拟这个过程。
所谓光线追踪应用了光线传播的一个性质,这个性质叫做reciprocity,这个性质是说光线的可逆性,就是说理解成从光源发出光线,然后再不断弹射打到人的眼睛,也可以认为眼睛发出一些感知光线,然后打到物体上最后打到光源上去。如果我在场景中找到一条光路,可以把光线反过来找到,仍然是一条可行的光路。这里呢引用一个诗,当你凝视深渊的时候,深渊也在凝视着你。这就反映了说,你能看到什么东西,镜子一类的,如果对面有个人在你看到的附体附近,那他一定看得到你,那就是把传播方向全部倒了过来。
光路的可逆性其实很早就有人研究这个问题,大家可以看到早期在欧洲这些科学启蒙时,有很多人会这么认为,说我们看到的东西是怎么回事呢,是因为我们眼睛会发出所谓“感知光线”打到世界中去,光线会被物体挡住,我们就看到东西。这个理论我们现在看过去荒谬,但是可以看到很多名人都在支持他。当然了这是一个对世界的错误描绘,但是在光路的可逆性下,我们可以这样理解。这就是光线追踪,追踪的意思。我们做光线追踪实则是从我们的相机出发,或者从眼睛出发,然后向世界中投射光线,然后把这些光线不断的弹弹弹最后再连到光源上去。我们事实上是这样做的。
我们继续这个话题,那么我们刚才说光线追踪利用了所谓光路可逆性,我们从光源出发是一种思路,所谓光线追踪呢,既然是追踪,从它的终点开始,也就是从眼睛或者相机开始。然后怎么做呢,首先我们做光线的投射,我们假设我们在往一个虚拟的世界中看,面前放了个所谓的成像平面在眼前,成像平面被画成了不同的格子,对于每一个像素,我们可以从摄像机连一根线传过这个像素,就打出一根光线,这根光线一定会打到场景中的某个位置或者和什么东西都不想交,如果和某一个物体发生相交,所谓我看到了哪,然后我把这个点和光源做一个连线,这是要判定这个点是不是光源也可见,也就是是不是在阴影里。如果不是,那我们就形成了一个有效的光路,从光源到这个点再到我们眼睛,那我就能计算能量,自然就能把最后看到的颜色算出来,计算出着色。所谓光线投射就在做这样一个事情,怎样去生成不同的光线。
咱们看一个这个实际的例子。在咱们做这个之前,整个光线追踪有一些假设,比如说认为眼睛永远是一个针孔摄像机,眼睛是一个点,一个位置,不考虑实际相机应该如何处理。就是说有一个大小的镜头,这种说路径追踪的时候会说。然后光源这里呢,我们也假设是个点光源,然后对于场景中的这个物体呢,光线打到他之后,会发生一个完美的折射或反射。就是说光线打到这个球,会沿着镜面反射方向反射出去。这是前提。我们现在看看,首先,所谓光线追踪,从眼睛开始,往场景中,成像平面中的任何一个像素,投射一个光线,会打到场景中的某个位置,我们连到这个光线,会打到一个球,然后呢这里我们管它叫eye ray,就是光线从眼睛出来的,或者叫camera ray。那肯定对每个像素都要这样做,那现在先针对这一个像素说,这是eye ray。然后呢我们说这条光线投射出来之后,会打到场景中最近的一个物体,对应这里
。从事实上来说,这个光线可以和很多物体相交。我们知道物体相互产生遮挡,考虑到我们人眼看到的肯定是最近的,因为会遮挡别人各种各样的点,所以这根光线我们永远考虑的是场景里最近的一个交点。我们只说了一点,如何去投射光线,我们已经完美的解决了深度测试的问题。大家可以想象在光栅化里面我们废了多少功夫去做深度缓存,每个像素不断记录最近的点。对于这个光线投射来说道理是一样的,沿着一根光线,我们记录离他最近的交点,怎么做之后再说。当我发现了一个交点之后,我们要考虑这个点会不会被照亮,我们把这个点从光源连一根线,这就是我们要连的第二根光线,我们把这根线叫shadow ray,判定光线沿着这根光线
中间没有物体阻挡,就知道光源可以照亮这个点。黑色箭头是法线,求出来一个交点后自然任何一个位置知道他的法线是多少。有了法线,有了入射方向(光源到物体),有了出射方向(物体到人眼),这个点着色算出来之后写入像素值,这是个很简单的事情。怎么做着色呢,各种各样的着色模型,bulinn-phong这些都是可以的。那么光线投射做了什么事情,每一个像素首先投出去一根光线从眼睛开始,然后和场景相交求最近交点,找到交点后和光源连线第二条光线,判定是否对光源可见,之后算着色,写回像素的值。
之前就是这么一种方法生成这个和光栅化能够得到近似相同的结果,但是你应该看到了,我们考虑这个光线打到场景中一个物体后求着色、阴影没问题,这个不还是考虑光线只弹射一次吗,我们说光线可以弹射很多次,那怎么办呢,那就是这里我们跟大家说的,这个课的主要内容就是所谓whitted风格光线追踪,他本身是个递归的算法,这是什么意思呢。首先来说,这个命名是由T.whitted这位发明的方法,这个方法可以明显看到这个左边玻璃球,以及类似镜子的球,左边的球基本做折射,右边的球做反射,通过这个球我还可以看到折射之后背后的内容。比如说光线打到球之后,发生一次折射,进入了球内部,再发生一次折射出来,再打到某一个物体上,打到他背后的物体上,再连光源做着色,那这样这个光线弹射了好多次,这个whitted方法可以做这个风格的光线追踪,当然可以得到一个不错的效果。当年渲染这一幅图需要74分钟,06年pc只需要6秒,现在对这个场景来说,如果放在实时光线追踪里面,用gpu应该可以做到几百帧几千帧每秒都是可以的。所以硬件的进步也是很惊人的,考虑到和当时的效果比较,那么whitted风格光线追踪到底是怎么做的呢。
我们还是从光线投射开始,光线投射可以每一个像素找到最近的这个点,然后呢我们考虑在这个点上,如果这个球本身是一个玻璃球,那我们知道当一个光线打到玻璃或者这种透明的介质上,肯定有两种情况发生,第一肯定有一部分能量反射掉,另外有一部分能量要折射进去。小时候都做过物理实验,激光打到水面,大家会发现一部分要反射,一部分要折射进去,这里一样。我们知道有多少能量会沿着反射方向继续传播,那也就是说我们从眼睛投射出来光线打到一个点的时候,如果这是个玻璃材质,那我知道我在这个点上应该要做一个镜面反射。我们假设反射都是完美的,这里就是whitted风格讲究的内容。
这条光线是反射光。
同样道理,这根光线到这里是个玻璃球,他还可以折射进去,他可以折射进去,在内部再传播,他还会打到另外一个点再折射出来。咱们看这一条所谓光路(反射)已经弹射了2次了,往上折射这条光路已经弹射了3次。也就是说可以弹射很多次,无限次,甚至可以永远这样做下去,这是没有问题的。就是说这个whitted风格说的就是这么个意思,在这么一个点可以继续传播光线,只要你正确的算出他的反射、折射方向。在whitted-style这个光线追踪里面,他所算的这个着色也发生点点变化,也就是说光线投射找到这个点之后
,这个点看是不是能被照亮然后我去计算他的着色,但在这里由于光线弹射的次数多了,我其实在每一个弹射的点我都会去计算他的着色的值。这目前每一个和场景中间的交点,我都去和光源做一条连线,我去看在这个点上,这光源是不是照亮他。这样一来,最后whitted风格光线追踪算了个什么事情呢,算了个我的光源如果可以照亮任何一个弹射的点,那我就弹射的点内部算出来着色的值都给加到像素的值里面去。
就是说我在A点算一个着色,在B点再算一个,这两点的着色都会被加到像素的值里面,这个很简单,因为这里已经定义了两种不同的光路了,一条是光源到A点再到人的眼睛,另一条是光源到B点再到A点再到人的眼睛。那光自然可以往各个不同的地方传播,所以这两个路径都是可行的,所以会把两个部分的结果都给加回去。同样道理这个地方还会发生一定折射,折射这里也是,
对于C点和D点会形成这么一条光路,以及D被挡住了就没有光路,如果有的话,也会把这点加上。对于光源到D点着色点,C点着色点,A点着色点最后到人的眼睛。他就是把着色的值全部都算进去。为了算着色,每一个弹跳的这个点,我都要连一条所谓的shadow ray,我来看他是不是在阴影里,是不是应该算最后的贡献。有同学反映说这里是不是有能量损失,当然了,不然经过越来越多次的弹射,可不是能量会一直累加,那最后看到的结果就是完全类似过曝的图,完全白的。在A这一点,原本能量过来100%,那么我们认为比如折射的能量(C点)占40%,B点占60%,那自然都是会考虑进去的。
我们再简单定义一下这个事情,就是说对于不同类型的光线,我们在这里做简单的归类,很简单,之前我们定义了camera ray(eye ray),就是从眼睛打过来的第一根光线往各个像素中间连的光线,那么这些光线又被称作primary ray。那么在之后的这些弹射,在一次弹射之后,我们都可以叫他secondary ray。然后我们区分下往光源的连线,区分可见性的,我们叫他shadow ray,这也是大家不同的分类,最好理解。这是基本上whitted风格光线追踪的做法,那么大家可以看到,这个最后结果,把这些光路的结果全部加到这么个点上去,我们就可以看到最后的结果。
从这个结果上,我们还可以看到其他的这些信息,比如说这个阴影(左边阴影)看起来没那么暗,右边阴影相对较暗。这个怎么来的,肯定是说我有一根光线从camera出发连向某一个像素,打到地板上的点,然后地板上的点呢它往某个方向去弹射,然后我会发现他会穿过这个玻璃球,还会接触到一定光照。从这个角度来说,对于镜子的球,他会挡住这个所有的的光照,对于玻璃的球,这个光线会有办法来穿过它,自然这里会收到更多的能量,当然它本身涉及到能量衰减,所以一定会投射出来一个阴影,只不过会非常淡。这样一来就可以看到这个所谓whitted风格光线追踪的好处,那么同学说这中间有很多技术问题,我们挨个之后解决。怎么判定光线是否被遮挡住,怎么判断光线投射出去会打到哪里,怎么样求所谓的反射光的方向和折射光的方向,以及他们各自带的能量,这能量得守恒,衰减该怎么算。无论怎样,刨除这些还没交代的技术细节,单说这个光线追踪的本身,所谓whitted-style这个还是挺好理解的,基本上就是模拟光线不断弹射的过程。到这里把基本原理告诉大家,那么一个一个来解决技术问题。
咱们要解决第一个技术问题,就是要求交点。在这个所谓着色各种各样之前,咱们肯定最早是光线投射出去之后要打到谁,也就是说交点怎么求。
我们要求光线和物体表面的交点。我们要求交点肯定要求各种运算,咱们先把数学上的光线定义出来。所谓光线,在数学上是个射线。有一个起点,有一个方向。我们说方向认为是单位向量。怎么定义呢,一个点光源,或者说是光线起点我们认为是O,他的传播方向是d,所以说我们用这两个量就可以定义一条光线,非常简单的事情,在光线上的任何一个点,我们都可以用O+td来表示,因为沿着一条光线,无非就是说从O开始往d方向走了多远,就是O+td。由于光线是射线,所以我们认为光线只能往一个方向传播,所以0<=t<∞。这个时候呢我们回到图形学很少考虑这种边界的条件,比如是大于0还是大于等于0问题不大。这简单定义了一个光线,任何一个沿着这个光线的一个点我都可以用O+td来表示。都是什么意思,任何一个点沿着光线叫r点,在t时间从起始位置+t*方向。
那么我们来说这个最简单的情况,光线如何与不同的物体求交,我们从最最简单的物体来说,光线怎么和球做交点,光线的定义我们刚刚说O+td,球的定义也是一个挺简单的定义,就是说我们可以把他的隐函数的表示写出来,就是说球上的任何一个点到他的球心的c的距离都等于半径。三维情况下这就定义了一个球的表面,那么我们现在要求光线和球的交点,那这要怎么做呢,我们想这样一个事,数学上我们如何定义一个交点,什么叫交点,交点就是说又在光线上又在球上,这才叫交点,既然这个点又在球上又在光线上,那他势必要满足两个函数。任何一个在球上的点p他同时也一定沿着光线走了某一个时间的,也就是说我可以O+td这么一个点减去球心算到球心的距离要等于R2。这个点又满足O+td,又满足(p-c)2-R^2=0。和在一块考虑,要解出
。这个交点现在O,d,c,R我们都知道,只有t不知道。传播多久可以打到球这个意思,现在我们自然而然可以解出来。
我们可以看到他无非就是一个二次函数,我们把他展开成
这个形式,这个二次函数怎么求解根呢。我们可以直接把他求解出来。那么根据解出来t的数量,首先要满足t得有物理上的实际意义,t得是正的。第二就是说解出来的这个根他要有实际的物理意义,他不能是个虚数,得是个实数。根据光线所在直线和球的关系,存在相离相切相交的情况,就可以解出不同的点。假如说相交我们可以求出两个不同的t,其中一大一小,求最近的肯定取小的。
对于和球的求交,无非就是解二次函数,咱们可以把这个问题给推广到光线和一般性的隐式表面求交。隐式表面的定义,就是定义这个点满足一个什么样的性质,我们定义一个点p,这个点使得某个函数会等于0,刚刚对于球来说也是如此。我们现在要求光线和这个隐式表面的交点,同样思考,这个交点一定又在光线上,一定又在隐式表面上,所以点p一定可以写成o+td的形式,所以可以写成f(o+td)=0,剩下的事情就变成了函数求解t,解出来记得两个事情,第一是实数,第二为正数。把这个条件加上,我们把值解出来,我们可以把不同的复杂隐式表面对应的表面和光线求交,而且很容易解得出值。目前来说数值计算的软件现在是非常发达的,就是说怎么样这么一个式子,现在很少关心这个事情,甚至不需要用什么数学的方法直接去求,直接用数值做优化,直接去求函数的根就可以了,这是很简单的事情。所以说对于一般性的隐式表面也是这样做的,解出时间t,解出t之后交点就是o+td。
那么我们对于一般性的隐式表面,对于显示表面怎么做,显示表面里面,最最重要的显式表面是什么,这些知识都是联系在一起的。对于显示表面,我们就说光线和三角形求交,是个非常重要的话题。光线如果知道如何和三角形求交,找到交点我能判断光线是否被遮挡,还有个很有意思的应用,我们把他列出来,通过光线和三角形求交的这么一个事情,甚至可以判断给你一个点,这个点是不是在这个物体内,或者这个点是否在这个物体外,这个是怎么做到的?大家可以试着在纸上随便画一个封闭的曲线,就是说正方形圆形都可以,在这个形状内部点一个点,随便往哪个方向去打一个光线,判断光线和这个物体有多少个交点,发现如果点在内部,交点数一定是奇数个,记住交点是射线。就是说随便取一个点,一个方向,定义一个光线,找交点数,如果得到基数个交点,那一定在物体内,如果偶数个交点,一定在物体外。如果定义在三维内,也是可行的。当然前提是物体本身得封闭不能有洞。回到光线和三角形求交,这是最最基础的操作,那么我们考虑一下如何做光线和三角形求交,最最简单的做法,光线和三角形面判定有没有交点,我就一个个三角形判断是不是和光线有交点。我可以求出来很多交点,最近的那个是我们要的。这个思路是对的。如果我要判断这个光线和物体是否相交,我就一个一个看。可想而知这是非常慢的一件事情,光线和三角形求交,一个物体要比如说10w个面,我整个分辨率4k,每个像素都要打一根光线,每个像素都要乘以10w这还得了?肯定有某种方法能加速这个运算,我们留到之后说。我们想象一下,就用这个简单的方法来做,任何一个给定的三角形和给定的光线球交点,要么得到0个交点,要么1个交点,同样道理,图形学上很少考虑平行情况。就比如说一根光线完美通过三角形并且通过它所在的平面这种情况,我们忽略这种情况。我们就考虑三角形和光线有1个交点或0个交点这个情况。
这就是我们要提到的一个重要话题,怎么样去做光线和三角形的交点。因为三角形肯定在一个平面内,那就把这个问题分解成两个问题,光线和平面怎么求交,找到交点之后再判定是不是在三角形内。这当然是可以的,我们就把问题进一步简化,光线和平面求交,在判定是不是在三角形内,我们知道如何判断是不是在三角形内。那问题就变成如何做光线和平面求交,这就回到最早的思路,如何做交点我们要先把物体定义出来,光线定义好了是O+td,现在开始定义平面。
平面我们可以定义成一个方向和一个点。就是说平面肯定有一个法线。如果定义了一个法线,还不够定义一个平面。定义好法线后,平面一定要过某个点。从例子上可以看出,想定义平面,给平面上任意一个点,再给平面法线就够了。只要平面上任意一个点p,满足p-p’和法线是垂直的就行了。大家可以想象,法线是什么意思,方向和平面垂直,那么和平面内任何一个线段垂直。那如果这个点在平面外,那和p‘一连线会发现这个向量和法线不垂直。所以只有在p在平面上时才能满足这个性质,那我们就能满足。如果大家把这个式子展开后,大家会发现他满足这么一个性质:这个很显然,是大家学过的平面的方程。也就是说这种定义通过直观的集合上垂直不垂直的定义,我们可以定义任何一个在平面上的点。所谓平面就是一系列这个p点的集合,使得p点和给定的p’点连线,和给定的发现垂直。咱们就用到了定义的这两个量,一个是平面的点,一个是平面的法线。
那么我们就来求光线和平面的交点,那怎么办呢,同样道理,这个交点一定又在光线上,一定又在平面上,所以点p又可以写成O+td的形式,所以回到刚刚的做法,大家可以看到我们把这个p换成光线的方程,那我们现在依然要求t,其他的量我都知道,t可以直接求出。解出这个t后,我们在判断,他基本上得是正的,得是实数。当然它本身就是实数,所有操作都是实数,那得确认他大于等于0。那这样我就能解出光线和平面的交点。下一步就判断这个点是否在三角形内就可以了。那么这个事情其实算是一个相对简化了的写法,就是说是个解答的思想,我怎么样去三角形和这个光线求交。那人们都是懒的,人们就在想有没有办法可以一下解出光线和三角形的交点,解出来后我立刻就能判定在三角形内,答案是可以的,那怎么做呢。
人们发现了一个这种方法,这个算法叫Moller Trumbore算法,我们就叫他MT算法好吧。这个算法在做什么事情呢?这个算法要解决的核心问题,大家看到我要求光线和平面的交点,那O+td这是光线,这个点肯定还在三角形内,如果这个点在三角形内,那我肯定可以写成用重心坐标描述的位置上,大家看到这个式子是不是很熟悉,因为这三个系数加起来正好等于1,而他们分别又是P0,P1,P2这几个量加起来,我们知道只要这3个系数加起来等于1,我们可以得到任意一个在三个点所定义的三角形所在的平面内的点。这也就是说这是用重心坐标表示的平面内任意一个点。然后呢我O+td是光线上的点,这两个既然是交点那就同时满足。那这样我就只需要解出时间t,b1,b2。这个式子解出来非常的容易,因为O,D这些量都是三维向量,点或者是方向,一个会表示3个数,所以我们有3个式子,3个未知量,当然可以求得解。解线性方程组,我们回到线性代数的概念,就是说怎么解一个线性方程组,有n个式子,有n个变量,我们要用一个东西,那就是克拉默法则,这个克拉默法则如果大家记得的话自然而然就可以理解,不能记的话,只要你能记住这个式子,知道有3个式子3个变量你肯定能解出来。大家会看到大家定义了好多东西,这个其实都是有明确的定义,无非就是你的输入,比如说三角形三个顶点P0,P1,P2,光线的起点O和方向D不同的组合,还涉及到叉乘。虽然比较麻烦但是解出来是非常直观的事。解出来后怎么办?怎么判定是否合理?第一沿着三角形方向,t得是正的,第二是我得知道点在三角形内。对于重心坐标表示的三角形所在平面上的一点,我如何知道是否在三角形内,那就是(1-b1-b2),b1,b2都是得非负的。就是说直接通过重心坐标就可以定义这个点是否在三角形内。说白了这是以另一个方法来描述这个平面是否在三角形内。这样一来光线和三角形求交我们就能一下得出来,不过本质上还是一回事。
到此为止,光线和三角形求交我们已经知道怎么做了,那么我们下面一步的问题是什么呢。还回到刚才说光线和三角形的面,他表示物体怎么求交,最简单的方法,把光线和每个三角形都做一次求交,然后再求最近的交点,找出最近最小的交点,很简单但是它很慢。我们要怎么提升速度?这就是下一个话题,光线和光面求交怎么样做加速。所为这个表面呢,我们说的更多三角形表面,用三角形形成的三角形面。
加速这个事情咱们刚才已经分析过了,这是必要的一件事,如果我们用我们之前最原始的做法,我们当然可以求出光线和整个场景的最近的一个交点,绝对没有问题,我们把所有的物体都考虑进去,所有的三角形数量都考虑进去,每一个像素我打出一根光线,和所有三角形求一次交点, 就是这么多次计算。如果光线在场景中弹射多次,那是不是每一次弹射我都得计算一次新的光线和整个场景求交,这已经慢的不行了,我们肯定不能这样做。
那怎么办呢,那我们想办法去提高他的渲染效果。这是一个非常复杂的场景,这个场景绝对不能用最之前的方法去做,这个场景及其的复杂,而这个场景是个经典场景,叫San Miguel Scene,他应该是有1000多万个三角形,你假设你就一根光线,每一根光线都要和一千多万个三角形求交,结束之后再乘以像素数,这还得了?特别还有细节这些东西,像像素叶子,这个场景及其复杂,当然还有玻璃的东西,这说明光线肯定弹射不止一次,这肯定是个更复杂的问题。
那另外一个呢,大家看这么一个场景,大家可以看到这是个室外的场景,各种各样的树叶、植物、草。大家可以看到草的叶子非常细,每个叶子可能都有好多个三角形组成,这里有两千万个三角形。这个如果用原始方法肯定是不行的,所以要想办法加速。
怎么样去加速,咱们提供不同的方法吧,为了加速,有一个很重要的概念,叫做包围盒,是这么个意思,或者说包围体积,这其实英文应该翻译成包围体积,这什么意思。
通过这个包围盒的形式,我可以做加速。先说明白这个包围盒是这么个意思,就是说我有个复杂的物体,我可以用一个相对简单的形状把他包起来,保证物体一定在这个简单的形状之内,这个二位的形状,大家可以看到我可以用某一种box,一个矩形把他给框起来;也可以用圆形把他框起来;我可以用一个简单的形状把他框起来,这就做成了一个包围盒。那这个包围盒有什么好处呢?大家把这个逻辑问题想清楚就好了,那就是光线连包围盒都碰不到,那更不可能碰到包围盒内的物体,这个逻辑虽然简单但是非常有效。那这就是人们引入包围盒的思路,为什么会引入包围盒的概念。
对于三维的情况,大家最常用的包围盒就是box,也就是一个长方体,这个长方体是什么意思。右边这是个长方体,平常我们的理解是,他是空间内的一个区域,他是x,y,z各占多少范围;那这次我们用另外一个方式来理解,我们理解成长方体是三个不同的对面形成的交集。所谓3个不同的对面,比如说长方体前后两个面我们认为是无限大的平面,这两个形成了一对面,这一对面会把中间的空间全部都给包含进去,形成了前后的对比。同样上下也是无限大的平面,也会形成上下对面。左右同样如此。那么现在回到定义上来,非常有道理,这个长方体是什么,就是三个对面形成的交集。我们通常在实际的应用中,我们为什么要引入对面这个概念,因为我们平常用到的包围盒通常叫做Axis-Aligned Bounding Box,叫轴对齐包围盒,缩写成AABB。这种轴对齐的包围盒什么意思,也就是说我们看长方体任何一个轴都是沿着某一个坐标轴(不是任意旋转的,对面全部平行于x,y,z)。对应到二维情况来看,可以认为是长方形,边一定是横平竖直的。实际会很常用这种表示,这样的话我们就非常好解释。那光线现在如何和所谓包围盒求交,咱们说逻辑就是光线和包围盒都不想交,和里面东西更不可能。
那咱们判定如何判定一个光线和一个盒子是不是有交点呢,这就是一个很重要的内容。左边我们先做一个简化,我不知道光线在三维情况下如何和盒子求交,我们来看二维情况,通常情况下人们会愿意降低一些思维上的复杂度。很简单的想法,二维情况下如果给你一个长方形盒子,理解成是两个对面形成的交际。我们先考虑x0,x1两条竖线,对于给定一根光线,我可以求出光线在什么时候会和这x0,x1这两个无限大的面有交点。那我们可以看到光线从O向d传播,在tmin时间和x0得到一个交点,在tmax时间和右边的x1有交点。那在两条水平的线(图2),我同样可以求出光线在什么时候会和y0,y1有交点。大家会看到从O传播往d去,那么一定在某个负时间和y0得到交点,在tmax和y1得到交点。而tmin呢,得出来一个不太合物理规律的时间,没关系,先假设光线是直线,最后再处理是射线的情况。那么我们对任意一个对面都可以求出光线进去和出去的时间。那么我知道这个光线什么时候分别进入这两对面,什么时候出去这两对面;那我们怎么知道什么时候进来盒子什么时候出来盒子呢。对于二维的情况(图3),是个简单的看法,大家知道在tmin时间进入,在tmax时间实际出去了,这要怎么理解呢,为什么说得到这么两个线段(图一和图二),事实上来说图三才是实际在盒子里时间?实际上求了两个线段(图一和图二)的交集。因为盒子本身是两个对面形成的交集。这到底是为什么?这就是我们要思考的问题,这个问题同样可以用逻辑性的事情来得到解决。我们直接看三维的情况,怎么样去看光线和盒子是否相交。
从三维情况来考虑,三维情况给个盒子,就是3个对面,这里就是说我们最最重要的一个思想,我们想光线什么时候算是进入三维情况的一个盒子,那我们自然想法就是只有当光线进入了所有的这些对面,就是说当光线把3个对面都进去了,我们才能说光线进入了盒子。那么我们说什么时候离开呢,只要离开任意1对对面,就离开盒子了。(在本节有牛牛图那张ppt,老师最后说了,图形学上很少考虑平行情况)咱们立刻可以得到一个算法出来,对于三维空间中,有3个不同的对面,x,y,z方向上,我各计算一次光线进入对面的最小时间和最大时间,什么时候进入什么时候离开。根据我们之前观察的结果,什么时候才能说光线进入了盒子呢?必须得进入所有的三个对面才认为光线进入了盒子,我对于所有的进入时间三组tmin求最大值,这是最晚最晚光线进入对面的时间,这是光线真正进入盒子的时间。同样时间,什么时候离开盒子呢,只要光线离开任何一个对面,光线就离开盒子了。什么时候光线离开任何一个对面呢,就是tmax求最小值。我们通过这个思路分析,明确得出光线在什么时候进入盒子,以及什么时候离开盒子。那么什么时候有交点呢?如果说进入的时间小于离开的时间,说明光线在这段时间内他就在这个盒子里,所以所谓stays a while,就是说在这个盒子里了一段时间,那当然就是有交点,反之就没有交点。到目前来说基本上是对的,但是现在还有些问题没有处理,那就是我们一直没考虑负的值怎么处理,我们一直考虑是一根直线和盒子求交点,那么开始考虑负值情况怎么考虑。
那么光线显然是个射线,我们首先检查时间t是正的还是负的。那我们现在有两个时间,一个是进入盒子的时间,一个是离开盒子的时间。
如果光线离开盒子的时间小于0,那说明盒子一定在光线的背后,这样想对吧,光线反向延长了才会发现tenter,texit都小于0。小于0的概念就是在背后。盒子在光线背后是不可能有交点的。
如果上种情况不满足,当离开时间是正的,进入时间是负的,这种情况说明这也是一个很常见的情况,大家可以想象给你一个盒子,这时候不考虑对面,我们已经把进入和离开盒子的时间考虑清楚了,如果说有一根光线传过盒子自然可以找到进入的时间和离开的时间,都得是正的。那什么时候会出现进入的时间是负的呢,那就只有一种情况,那就是光线起点就在盒子里。这种情况光线在盒子里,我要判断光线和盒子是否有交点,这肯定有交点。那我们就把所有的正负情况考虑清楚了。前提是在有交点的情况下。
总结起来,光线和AABB有交点,当且仅当,离开时间非负,这样可以得到最后的结论。
还有一些细节问题我们来处理一下,比如说我们刚刚说用Axis-Aligend bounding box,横平竖直的对吧,为什么这么做,因为光线和这些坐标轴平行的面求交点好求。如果说光线本身和这个盒子平面和轴是平行的,这种情况就很简单。比如p’和yz平面平行,所以p’平行于x轴,所以直接看x轴上的传播,从o+td会和这个点得到一个交点。那么对于这种情况下来说,如果说我只看x轴方向的传播,其实就够了。我用p点的x坐标减去o点的x坐标,这段距离我直接除以d这个方向的分量,这样可以立刻得到在时间t在什么时候可以认为光线打到了这么个板,就是利用了下当这个轴,这个平面和各种各样的轴平行的时候,会相对容易点。计算量的对比,下方很简单。