Ray Tracing1
Ray Tracing1 (Lectures 13~14)
内容:
-
为什么需要光线追踪?
-
Basic Ray-Tracing Algorithm
- 光线追踪对光线的假设
- Ray Casting
- Recursive (Whitted-Style) Ray Tracing
-
一些问题
- 求光线的交点 Ray-Surface Intersection
- 隐式求交
- 显式求交
- 求光线的交点 Ray-Surface Intersection
-
“包围盒”加速求交
-
(均匀)空间划分 Uniform Spatial Partitions
-
Spatial Partitions
-
Object Partitions &
Bounding Volume Hierarchy (BVH)
Why Ray Tracing?
光栅化渲染本身有些问题不能解决,例如软阴影,光线多次弹射的处理问题。同时,尽管光栅化很快,但是本身渲染出的结果质量并不高。

光线追踪与光栅化二者渲染的比较:

- 光栅化是实时的,而光线追踪是离线的,但是最近又出现了实时的光线追踪
- GPU的发展加速了光线追踪的渲染。
Basic Ray-Tracing Algorithm
光线追踪对光线的假设
- 光沿直线传播,不考虑光的波动性
- 认为两条或多条光相遇时,每条光的性质不会改变,传播方向也不会变,仍然不考虑光的波动性
- 光路可逆,光线从光源经过各种反射、折射到达人眼等效于人眼发出光线经过物体的反射、折射最终到达光源。(光线追踪的核心思想、体现在追踪)

Ray Casting
第一步:光线投射

- 光线追踪假设眼睛是一个点、仅仅是一个位置、不考虑大小(路径追踪考虑)
- 认为光线与物体接触发生的反射和折射是完全的
- 一条光线从眼睛出发,穿过成像平面的一个像素(等效理解为成像平面中的一个像素投射出一根光线、每个像素都要这么做),与一个物体的表面发生接触,考虑光线与场景物体的最近的一个交点(之后的物体因为光线被最开始的物体挡住所以看不到,即解决了光栅化中深度测试的问题,不用涉及到深度缓存)
- 对最近的交点往光源连一条线,如果这条线上没有物体阻挡,则光源可以照亮这个点,如果有的话这个地方就是在阴影里,然后在这一点着色,然后将其写入像素的值。
- 每个像素投出一条光线,与物体交与一点,然后判断这一点是否可见,最终着色写入像素的值
- 最终结果与光栅化差不多
- 但是光线也只弹射一次,(光线弹射多次即whitted风格的光线追踪)

Recursive (Whitted-Style) Ray Tracing
- 考虑到物体是一个玻璃的,光线穿过去发生反射或者折射,然后再对物体作用,如果是玻璃的话则会继续反射、折射,不断递归(Recursive)

- 首先同样考虑像素投射出一条光线,这个光线到达透明的玻璃物体的最近接触点会发生反射与折射

- 一部分能量在这一点上会发生完全(与光线投射一致,whitted风格假设内容)的镜面反射,一部分能量折射进去折射出来,当然根据情况例如后面的物体也是玻璃,可以多次的反射、折射下去。
- whitted风格指的是在任意一点可以继续传播这条光线,前提是算出折射方向,反射方向
- whitted风格着色也有变化,在每个弹射点都会与光源做一条连线,看看这个点是否能被照亮,然后对所有能被照亮的点进行着色 然后将所有的着色 加起来写入像素

- 以上对应了三种光线,仅仅是方便分类
- 考虑全局效果的光照模型,因为不仅仅考虑了直接光源的贡献,还考虑各种折射与反射光线的贡献
- 对所有像素做同样的操作便可以得到一个成像图
一些问题
求光线交点 Ray-Surface Intersection
隐式平面求光线交点
光线的定义:
- 光线等同为一条射线,起点为o,传播方向为d

光线与球求交点:
- 同时满足光线的定义以及球的定义

联立求解:

- t>0,且有实际意义,不能是实数
- 相离、相切、相交对应直线与圆的位置关系解法,解二次函数
- 求交取的是最近的点,t最小
推广到一般性的隐式表面求交

(一个交点)
显式平面求光线交点
光线与显式表面求交,通常是三角形面。
用处:
- 物体的可见性
- 判断几何点的位置,点在物体内部引一条射线有奇数个交点,在外部的话就有偶数个交点,推广到三维也适用。

光线与三角形面求交基本想法:
- 考虑物体上的每一个三角形面
- 对每一个三角形面都与每个像素发出的一根光线进行求交判断,结果要么是0,要么是1
- 不考虑三角形面与光线平行的情况
- 计算当然慢,有其他的优化方法。
光线与三角形求交:
- 光线与三角形所在的平面内求交
- 点是否在三角形内
- 先解决光线与平面求交
平面的定义:

交点又在光线上,又在平面上,两个方程联立求解时间t,从而得到光线上的交点。

- 得到参数t之后,自然可以计算出交点,再去计算出重心坐标就能判断该交点是否在三角形内了
另一种直接的方法: MT算法

- 对于上面的式子,等号左边表示光线上的点,右边表示由三角形的重心坐标确定的三角形平面内的点
- 光线与三角形相交意味着交点同时满足
- 有三个未知数,因为点是定义在空间上的三维的。
- 对应线性方程组,Cramer法则
- 解出来就是图上定义的方式
- 判定解的合理,t首先是正数。重心坐标的三个系数都是非负的,才能确保点在三角形内。
一般表面求交考虑的是三角形面与光线求交点
但是同时对应一些问题

bounces:光线弹射后的求交

Bounding Volumes 求交加速
- 用一个相对简单的物体包围住一个复杂的几何物体,保证物体在简单的几何物体内。
- 如果光线没有接触到包围盒,那么里面的物体也不会碰到

三维的常用的包围盒--长方体,求交点好求
将长方体理解为三个不同的对面(平面认为是无限大的)形成的交集,且一般这个长方体的每个轴都沿着坐标轴。

光线与包围盒求交:
二维情况引入:

- 首先在2D情况下,Y轴上的两个对面与光线有两个交点,对应这一个较大的时间以及一个较小的时间。
- 同时,但是在X轴的两个对面下,同样能得到两个交点,但是其中一个交点是负值(因为将光线当成直线处理了),负值的情况在3D的时候讨论。
- 现在已知两个对面的四个时间,那么怎样知道光进入的时间以及出去的时间。
- 由图能直接看出,两条线的交集部分与两个对面形成的交点对应的时间就是光进去和出去的时间,且时间短的对应进去,时间长的对应出来
三维情况:

-
首先明确,光线进入这个物体当且仅当光线进入了三个对面.
-
又因为是进去,三个面的t与它们的各自的另一个对面的t对比,它们三个的t都是小的。
-
在三个小的t中找最大的那一个,就是光线进入这个物体的时间。
-
当光线从一个对面出来的时候,就认为光线从这个物体中出来了
-
每对对面都有与光线相交时都有一对时间,一个是小的,一个是大的,大的意味着光出来的时候与另一个对面相交的时间,从大的时间中找出小的那个就意味着光从物体出来了。
-
负数稍后讨论。

即:如果进去的时间如果小(早)于出去的时间,那么就意味着光线在其中有停留。

考虑到光线不是直线那么则会对正负进行讨论,如下,直观能理解。

包围盒与坐标轴平行的好处--简化计算

均匀空间划分 Uniform Spatial Partitions (Grids)
仅仅利用包围盒进行加速是不够的,还要进一步的处理。即均匀空间划分。
当一个场景中只有一个物体时,利用包围盒进行加速实际上相当于没有加速。
当一个场景中物体的数目较多时,也涉及到多个包围盒求交判定,光线追踪的加速效果也有限。
进一步的,用包围盒处理一个考虑的场景,再对这个包围住场景的包围盒再次均匀划分为更小的包围盒。然后进行判定。
一般认为光线与盒子的求交速度是十分快的。
-
将包围盒划分为一个个小盒子(小的AABB),找出放有几何物体的盒子,不关心几何的包围住的内部(预处理)
![avatar]()
-
找出光线经过的格子,如果里面有物体的话,做一个求交判定,从而简化计算。
-
光线的路径问题,一个简单的思路,光线往右边走,下一个走的地方要么是右边要么是上面,对两个格子进行求交判定。
-
将包围物体的空间进一步划分为多个均匀的小的AABB,再根据光线方向找出相交grid,再判断grid中是否存储了模型信息,若有则进一步求交。
-
格子的划分数量是影响性能的关键。
-
格子不能太密,也不能太疏。有些算法确定格子数
当一个场景中的几何物体分布较均匀的时候,空间均匀划分的效果较好。
每做几次求交便可以得到一个物体交点。

当一个场景中几何物体分布不均匀的时候,有许多的无效的格子浪费储存以及做太多次无效的光线-格子求交。即使一次的所需时间不多,但是多次无效的次数加起来也是浪费。

这种方法在某些应用上挺好用,例如通过GPU硬件实现的算法。
均匀划分对应的缺点,可以通过不均匀划分来解决。
空间划分 Spatial Partitions (Grids)
针对均匀划分的问题:在几何物体的稀疏的地方,用较小的大格子去包住这块区域即可,几何物体密集的地方,就多用些小格子。
常用的空间划分:

-
八叉树(Oct-Tree),三维情况下对空间切三刀,得到八块,对于每个小空间(子节点),再切三刀得到八块空间,不断循环,可以定义一个规则来停下分割空间(得到的不均匀的空间块---每个空间对应的几何物体数目不一样,停止切割时机也不一样)。对应二维情况下就是四叉树了。一般不常用,因为划分与维度有关、二维是四叉树、三维是八叉树,位数再高点对应十六叉树.....为了解决维度的问题,引入KD-Tree
-
KD-tree:
- 三维情况下,先沿着x轴切一次得到两个空间,然后再对得到的空间沿Y轴切每个空间又形成两个空间,然后对Y轴形成的空间沿着Z轴又切一次,然后 xyz不断循环切。最终形成的类似与二叉树。终止条件同八叉树类似,根据空间的几何物体不同停下来的时间也不同。
- 二维情况下如图,先对空间X轴方向切一次,得到上下两个空间,再对上下空间沿着Y轴切又得到新的空间,不断循环知道满足停止的条件。
-
BSP-Tree:同KD-Tree,只不过切的方向是任意的,光线求交会变得困难。(不同与KD-Tree类似的AABB性质)
KD-Tree组织加速结构处理过程:
只用一半的空间划分做例子,其余的仍然同样处理只不过没表示出来。



- 中间结点的存储
- 要知道当前结点沿着哪个轴划分、下个结点的划分方向就是对应规则的下一个
- 划分的位置
- 中间结点一定有两个子节点
- 对应的终止划分条件
- 实际物体或者说三角形是存储在叶子结点上的。
建立好的加速结构如何对光线追踪进行加速:
-
光线如果对某个格子没有交点、那么就不用管,如果有交点的话,那么光线可能与它的两个子节点可能有交点,分别判定。直到划分到叶子结点,如果没有就不用管,如果有的话就进行光线与叶子结点中的三角形求交。
-
如果光线与某个结点都没有交点,那么就可以忽略掉这个结点底下的所有子节点的求交判定。

简化处理:光线与这个场景有交点(根节点),那么考虑这个根节点以下的两个子节点是否有交点。图中将蓝色区域认为是根节点了,只是一种简化处理。光线与蓝色判定有交点,那么进一步的会与叶子节点里面的几何求交判定。同时光线与B也有交点,B的子节点对应绿色的和C,绿色认为是叶子结点,光线与绿色的有交点,进一步也会与里面的几何物体求交判定,对于来说也有交点,C的子节点是D和橙色的部分3,求交发现光线与D根本没有交点,那么D后的一切子节点都可以忽略,光线与3这个叶子结点有交点,那么就与里面的几何求交判定。整体的一个加速判断思想用一个简单的场景例子说明。
- 实际上,KD-Tree的实际建立中,判断三角形与AABB空间(包围盒)的交点情况目前仍存在问题。同时,一个三角形可能与两个AABB空间都有交集,一个物体有可能存储在多个AABB中。因此通常不是首选方案。
Object Partitions & Bounding Volume Hierarchy (BVH)
既然对空间划分有诸多问题,那么就通过物体来划分,这种划分形成的加速结构称为BVH。在目前图形学中有广泛应用。
- 一开始给个场景,用包围盒包起来,对应根节点
- 对物体进行划分,以某种规则将场景中的几何物体(三角形)分成两堆,作为根节点的子节点。
- 分成两堆后再重新计算包围盒,当然包围盒可以相交,但是里面的几何物体确实单一属于一个空间,解决了KD-Tree的一个问题(一个三角形只属于一个空间)。
- 更进一步,对划分之后的区域再次进行划分,划分之后再求包围盒,直到到达某个规定的条件,如划分到只有五个三角形
- 划分方法也可以按照KD-Tree划分方法,先左右,后上下等方式。
- 三角形划分两堆方法不难进行,求包围盒同样不难,避免了三角形与包围盒判定求交的问题。
- 这些包围盒可以相交、这也就意味着空间并没有完全的划分开,仍然有交集的部分。物体划分方法应尽可能的让重叠部分少。划分的方法值得研究。
- 核心方法:分堆、求交、不断递归
- 划分到三角形的个数较少时便不再划分,将三角形的信息储存在叶子结点中,中间节点只存包围盒以及对应子节点指针。
- 动态的情况重新计算BVH。



为了让结点在空间中有均匀的分布、划分的方向沿着最长的轴分为两半。
如何把物体分成两堆,一种思想是:从中间的那个三角形进行划分(所中间是三角形序数的中位数)保证的是划分后的两堆三角形数应一致,保证树的分布更平衡,深度搜索 对应搜索次数少。
既然涉及到三角形的序数就有对三角形的排序问题。一种想法是取三角形的所有重心坐标,假设对X轴划分,那么就取重心坐标的x轴排序,找中点。当然,有快速选择算法O(n)。
对应的数据结构:

光线与BVH求交,也就是生成了加速结构后怎么用它。
对应的伪代码:

不难理解,方法和KD-Tree一样。
BVH划分和空间划分的图形直观对比

整个Whitted-Style的光线追踪的内容,基本上涉及了光线追踪思想、几何求交、加速结构、加速结构的应用。给出的是基本的思路想法,代码实现有对应的书籍资料。

对应的how反而是次要的问题了。
reference:
[1] GAMES101-现代计算机图形学入门-闫令琪


浙公网安备 33010602011771号