源码学习网 首页 编程学园 ue4教程 查看内容

光线追踪

2019-7-16 22:00| 发布者: opiye| 查看: 74| 评论: 0

摘要: 内容有点多,所以错误,遗漏不可避免,希望大家多多指出。其他重要的部分日后会补上光线追踪本文首先将会高屋建瓴高瞻远瞩的介绍光线追踪的类别族谱,介绍其公共部分,之后我们会分别对其中的每一个进行详细的剖析。 ...
腾讯云服务器秒杀

内容有点多,所以错误,遗漏不可避免,希望大家多多指出。其他重要的部分日后会补上

光线追踪

本文首先将会高屋建瓴高瞻远瞩的介绍光线追踪的类别族谱,介绍其公共部分,之后我们会分别对其中的每一个进行详细的剖析。

光线追踪(Ray tracing)是一个拥有历史感的词汇。 图形学从业者从 neutron transport , heat transfer 和illumination engineering 等领域引进其思想。 由于这些概念被许多领域所研究,导致光线追踪的术语在学科之间和学科内部都进行着不断发展,有时也会产生分歧。经典的论文也可能会错误地使用术语,这就可能会造成混淆。所以,在介绍光线追踪的概念之前,我们需要分清楚这些术语的概念。

几乎所有的现代光线追踪器都是使用递归和蒙特卡罗方法; 但是现在已经很少会有人把他们称为“递归的蒙特卡洛”方法。

一.光线追踪族谱

1. Ray Casting.1968年,Arthur Appel。

Some Techniques for shading machine renderings of solids.papalqi.cn

2.Whitted 光线追踪。1980年,Whitted ,Kay和Greenberg 提出了使用递归的光线追踪来描绘准确的折射和反射。

An improved illumination model for shaded displaypapalqi.cn

3.分布式光线追踪。1984年, Cook 等人提出了分布式光线追踪(distributed or distribution ray tracing,DRT)。 但是,这种方法通常被称为随机光线追踪(stochastic ray tracing),来避免与分布式处理(distributed processing)混淆。其和path tracing都使用了蒙特卡洛方法。

Distributed Ray Tracingpapalqi.cn


4.路径追踪,1986年引入了两个重要的算法.Kajiya将积分输运方程(integral transport equation)称为渲染方程(rendering equation)。他提出的渲染方程成了至今为止几乎所有的全局光照算法的数学依,在同篇论文中他也提出了最原始路径追踪算法。

THE RENDERING EQUATIONpapalqi.cn



5.双向路径追踪(BidirectionalPath Tracing)由Lafortune和 Willems提出,Veach则对双向路径追踪做了详细的描述。

Bidirectional Estimators for Light Transportpapalqi.cn
Optimally Combining Sampling Techniques for Monte Carlo Renderingpapalqi.cn

6.Metropolis Light Transport,1997年 Veach首次将最早应用在计算物理领域的 Metropolis 采样方法引入图形学

Metropolis Light Transportpapalqi.cn


7.Energy Redistribution Path Tracing 2005

Energy Redistribution Path Tracingpapalqi.cn

8.Manifold Exploration 2012

Manifold Explorationpapalqi.cn

二.Ray Casting

第一个用于渲染的光线投射算法最初由Arthur Appel在1968年引入。光线投射通过从观察点对每一个像素发射一条光线并找到在世界场景中阻挡光线路径的最近物体来渲染场景,raycasting只有两种射线,第一种是眼睛发射的eye射线,来寻找场景中的交点,另一个是从交点发到灯光的阴影射线,看自身是否是处于阴影当中。 与传统扫描线渲染算法相比,光线投射的一个显着优点是能够处理不平整的表面和固体。 电子世界争霸战(1982年电影Tron Series)的大部分动画都是使用光线投射技术渲染的。

早年间光栅化技术还未流行时,也会用于游戏当中。最著名的一款光线投射的游戏是Wolfenstein 3D。接下来将介绍不论在光线追踪还是光线投射算法中都通用的公共部分,避免之后的内容太过的庞杂。

2.1 光线投射基本算法

Render()  for each pixel x,y  color(pixel) = Trace(ray_through_pixel(x,y))

首先做的就是创建光线,得到每一个点的光线数据结构,之后开始Trace,这里使用Trace这个函数名是为了和后面统一。是指在场景中追踪这个光线。

Trace(ray)   object_point = Closest_intersection(ray)  if object_point return Shade(object_point, ray)  else return Background_Color

我们在场景中寻找光线与场景中的物体是否有交点,并返回距离我们最近的那一个object的信息,如果找到,我们进行着色如果没找到我们返回设定的背景颜色。

Closest_intersection(ray)  for each surface in scene    calc_intersection(ray, surface)  return the closest point of intersection to viewer 

在光线投射的Closest_intersection中,我们并不需要额外的返回其他的复杂信息。

Shade(point, ray)   to calculate contributions of each light source

光线投射的着色,你可以自由发挥,最初的实现中也只有阴影的划分。对于像Wolfenstein 3D这样的实时游戏中的场景,一般是使用了额外的场景信息,来提高性能。总之这里的shade并不重要,主要是理解RayCast这个思想框架,而之后的所有算法都是在这个框架中的发挥。

当然我们可以也可以进行简单的着色计算,这就不进行递归的RayTracing是一致的,我们放在之后来做对比。

2.2光线的表示和求交问题

不论对于哪种的光线渲染算法,第一步都是进行从相机进行光线投射,其最终的目的就是找到屏幕中的每一个像素对应的光线的方向。



在这里,对于局部、世界、相机空间的转换关系是和光栅化渲染中的空间转换的做法基本类似,所以在这里就不进行赘述。

大多数情况下,我们使用 [公式] 的方法来进行光线的表示,e是我们设定的相机观察点,而s是画布上的点,通常我们渲染之前预先设定的长宽,然后我们通过参数t来表示光线的长度。

  1. 当t为0,表示原点
  2. 当t为正数代表正向我们的方向
  3. 当t为负数代表在原点后面
  4. 如果t1<t2,代表t1离我们更近

对于求交问题,网上非常多的资料,这里就不讲了,这里只给出思路和参考资料

这里有一个对于物体来说是显示表示还是隐式表示法的问题。

三.Whitted RayTrace

光线投射渲染的进化发生在1979年,当时Turner Whitted通过引入反射,折射和阴影来延长光线投射过程从而形成自身的Whitted光线追踪。

在当时它得到的那张512x512的渲染图耗时74分钟,而今天则只需要几秒钟就可以完成。

大致上,Whitted RayTrace其主要把光线分为四种

  1. 视角光线,和之前的一样,没什么好说的
  2. 反射光线。在表面沿镜面反射方向继续照射。反射的颜色由反射光线与场景中的对象的交点决定。
  3. 折射光线,其创建与反射光线类似,只是它的方向是进入对象并最终可以退出对象。
  4. 阴影光线,是通过创建从交点到所有灯光的阴影光线来计算的。如果阴影光线在到达灯光之前与某个对象相交,则该交点将从该特定灯光中阴影显示。

Whitted 光线追踪主要解决了场景中没有间接光的问题,但是它解决的也相当的一般,因为它所有的间接光都只来源于完美的镜面反射或者是折射,这种材质显然在现实世界中并不常见,对于大多数的间接光基本无法模拟,这也是由于它对每一个EyeRay 的交点只发出一条的Reflected ray和Refracted ray。这个问题将在分布式光线追踪中有所改善。

3.1.光线追踪的类型

3.3.1. 前向光线追踪 Forward Ray Tracing

前向光线追踪遵循光子从光源到物体。虽然前向光线可以最准确地确定每个物体的颜色,但效率非常低。这是因为来自光源的许多光线永远不会通过视平面并进入眼睛。追踪来自光源的每条光线意味着许多光线将被浪费掉,因为它们从未对从眼睛看.(这里的前向后向可能每个人理解不同而相反,这里指的是Light Ray Tracing)

3.1.2. 向后光线追踪 Backward Ray Tracing

为了使光线追踪更有效,引入了后向光线追踪方法。在后向光线中,在眼睛处产生眼睛光线; 它通过视平面并进入世界。射线击中的第一个物体是从视平面的那个点可见的物体。

后向光线的缺点是它假设只有通过视平面并进入眼睛的光线对场景的最终图像有贡献。在某些情况下,这种假设是有缺陷的。例如,如果一个透镜被固定在桌子顶部的一个距离处,并且被正上方的光源照亮,那么在透镜下面会有一个具有大光浓度的焦点。如果反向光线追踪试图重新创建此图像,则会计算错误,因为向后发射光线只会确认光线通过透镜;反向光线无法识别通过透镜的正向光线弯曲。因此,如果只进行反向光线追踪,透镜下方将只有一个均匀的光斑,就像透镜是一块普通的玻璃。

如上所述,有效且最容易实现性能优化的一种方法是从眼睛向后发射光线,而不是从光源光发射线。通过这种方式,不会浪费计算能力来从未击中模型或相机的光线。

3.1.3混合光线追踪

由于前向光线追踪和后向光线追踪都有其缺点,最近的研究试图开发出会影响速度和精度的混合解决方案。在这些混合解决方案中,仅执行某些级别的前向射线。算法记录数据,然后继续执行后向光线追踪。场景的最终着色将后向光线和前向光线计算都考虑在内。

Veach(1995)发明出了后向光线追踪+前向光线追踪+连接线(Bidirectional Path Tracing)的混合方法,这在后面会讲解。

综上,而我们的whitted光线追踪一般而言都时使用的向后渲染的方式来进行的(从眼睛发射光线)。

3.2光线追踪基本算法

我们在第二部分聊的Shade部分,在这里就有了用途。我们知道递归是光线追踪的最基本的特点,我们先来看没有递归的情况。其实没有递归的情况和RayCasting技术特点几乎是一模一样的。

之前的框架是一样的,所以这里就不列出来了,这里只列出不一样的部分。

Shade(point, ray)   calculate surface normal vector  use Phong illumination formula  to calculate contributions of each light source

首先如之前所列的年代关系,出现Whitted光线追踪时,并没有提出我们现在做全局光照的渲染方程,所以当时依旧是使用的基于经验的光照模型,就像这里的phong。当然你也可以用其他的。

我们来看递归的形式:

Shade(point, ray)  radiance = black; /*初始化 */  for each light source    shadow_ray = calc_shadow_ray(point,light)    if !in_shadow(shadow_ray,light)        radiance += phong_illumination(point,ray,light)    if material is specularly reflective         radiance += spec_reflectance * Trace(reflected_ray(point,ray)))    if material is specularly transmissive        radiance += spec_transmittance * Trace(refracted_ray(point,ray)))    return radiance

首先进行这个点的颜色初始化,如果不在阴影中,我们phong模型的着色,如果材质拥有镜面反射,最后的颜色要加上镜面反射光线的颜色贡献,如果材质拥有折射特性,加上折射的贡献。

当然,这里对于放射和折射的具体计算比较基础,就不表了。搞了这么多,我们先不进行其他的分析,先来看看效果上和RayCasting的区别

递归两次
递归三次

还有一个比较重要的问题是什么时候结束这个罪恶的递归?有两种情况。第一种情况是,光线没有打到物体上,第二种是由于每一次反射或者折射,贡献值会逐渐的降低,我们预先设定一个阈值,当它小于这个阈值的时候,我们停止。当然,如果你只按照上面的做法来渲染一个图片出来是可行的,但是会有一些需要解决的问题。

3.3锯齿


我们首要问的问题时,为什么会产生锯齿?由于之前的光线追踪算法对每一个像素值,都只创建了一个光线,都只采样了场景中的一个点,和那一个颜色,但是,对于一个像素而言,有可能包含了很多个不同的点,尤其是在物体边缘的情况下时,这些点不一定都有相同的颜色。而我们这种有规律的采样,就会导致这种锯齿。

3.3.1超级采样

超级采样是为每个像素增加的光线数量的过程。这不能解决锯齿问题,但它会尝试减少它们对最终图像的影响。在以下示例中,从像素发出九条光线。六个是蓝色,三个是绿色。像素的最终颜色将是蓝色的三分之二和绿色的三分之一。

当然也可以有其他的组合方式,数量位置和比例上都可以自行设定。

3.3.2自适应超级采样

自适应超级采样(也称为蒙特卡罗采样)是一种以更智能的方式进行超采样的尝试。首先发出固定数量的光线并比较它们的颜色。如果颜色相似,则程序假定像素正在查看同一个对象,并且光线的平均值被计算为该像素的颜色。如果光线颜色不同(由某个阈值定义)那么我们认为这个像素比较特殊需要进一步检查。在这种情况下,像素被细分为更小的区域,并且每个新区域被视为一个完整的像素。这个过程再次开始,同样的固定光线模式被射入每个新的部分。

遗憾的是,自适应超级采样仍然将像素划分为规则的光线模式,并且会受到常规像素细分可能出现的混叠的影响。例如对象和采样网格机会是对齐的。总之有规律的方法都不得劲。

3.3.3随机采样

Stochiastic(随机)采样将固定数量的光线发送到像素中,但确保它们是随机分布的(但或多或少均匀地覆盖该区域)。此外,stochiastic射线试图解决在凹凸不平的表面上跟随入射光线的问题。 这是分布式光线追踪中比较核心的概念,所以放在下面来说。

3.4 加速结构

加速结构用于限制要检查的对象数量以找到与谁相交的技术。例如,如果我们有一条射线,它将一个物体与数千个物体相交,我们想以某种方式智能地清除远离光线的物体。

3.4.1包围盒(Bounding Volumes)

基于层次关系的划分方法中包围盒层次结构最有代表性。层次包围盒算法的基本思想是:用形状简单的包围盒(如球形、长方体等)将场景中的面片包围,相邻的包围盒被包含在一个更大的包围盒里,逐级扩大,生成一个层次的结构;在进行光线与物体相交测试之前先进行光线与包围盒的测试,如果光线与包围盒相交,再与其包含的物体面片进行相交测试,提高效率;通过将包围盒按照有效的层次结构进行组织,减少进行相交测试的数目,降低复杂度,进一步得到效率上的提升。在包围盒的选取中,一方面应该选择形状简单的包围盒,降低光线与包围盒相交测试的代价;另一方面应该选取能够紧密包围物体面片的包围盒,提高光线与包围盒进行相交测试的有效性。在实际应用中,最常用的是轴对称包围盒(Axis Aligned Bounding Box,AABB)。AABB轴对称包围盒简便,易于存储,易于计算,鲁棒性好,是对相交测试简便性和包围物体紧凑性的较好折中,效率较高。与基于空间分割的划分方法相比,基于层次关系的划分方法在动态场景中更有优势。因为,在动态场景中,场景的组织结构都要变化,每一帧都要重新构建,这无疑是巨大的开销,导致计算量激增。在动态场景中,基于层次关系的划分方法形成的数据结构,可以只更新相关包围盒的有关信息(如其位置和大小)。也就是说,BVH只需要进行数据结构的刷新,而不需要进行重建,这样在效率上有明显的提高。

  1. 快速拒绝:首先根据包围体检查光线
  2. 更快的拒绝:检查视锥体对象的包围体

一种组织结构是在将包围体分组到包含的包围体中,以创建自下而上的树层次结构,在命中包围体时,递归检查其子节点。

3.4.2均匀网格

空间网格是把三维空间分别沿着三个方向轴以特定宽度划分,得到一定分辨率的网格,场景中的面片都被分配到相应的网格中。每个网格可以含有不同的面片,每个网格保存其所包含的场景面片的引用。最简单的便是均匀网格,把场景进行均匀划分。

生成均匀网格并创建一个结构,将每个网格位置与网格位置中的另一个对象链接起来。对于光线接触的每个网格,检查对象是否与光线相交。

这种方法的优点便是方便简洁,易于创建,并且可以快速将场景中的面片分配到相应的网格中。然而,在实际的场景中,面片的分布是比较不均匀的,比如大多数的面片集中在少数几个网格中,则当进行光线遍历时需要遍历很多面片,也有可能一次相交测试能够踢除的无关面片非常少,遍历过程很麻烦。所以,如果场景中面片分布不够均匀的时候,这种分割方法的遍历效率非常低。

3.4.3 KD-Tree

BSP树是一种空间分割技术,在许多领域都有所应用,于20世纪90年代被引入到计算机图形学的各个研究领域和应用场合。KDTree可以看作是BSP树的一种特例,它是推广到多维空间下的树形结构。原理是将整个场景作为一个树,通过分割平面将当前树划分为两个空间得到两个子树,这两个子树又分别被各自的分割平面分割得到更小的子树,直到树的深度达到预定的阈值或者节点中含有的场景面片数小于预定的阈值。树的每个节点表示一个子空间,囊括所代表空间中包含的所有面片,树的根节点代表整个景空间。

以后单独讲这一块,下面的东西还非常的多,主线上不能丢下。

四.分布式光线追踪(distributed ray tracing,DRT)

我们终于即将触摸到现代的使用蒙特卡洛方法的光线追踪方法-分布式光线追踪。分布式光线追踪不是分布式系统上的光线追踪。分布式光线追踪是一种基于随机分布采样的光线追踪方法,用于减少渲染图像中的瑕疵。起初我们叫distributed ray tracing,后来叫它distribution ray tracing,为了区分parallel computing。我们根据特点也称其为stochastic ray tracing。为什么叫这个名字呢,看下去就知道了。那我们就有一个重要的问题出现了:我们为什么不满足whitted 光线追踪的效果,要不断的发明创造呢?

观察上图,随便一个普通人都可以非常清楚地断定,这是一张计算机渲染出来的图像而不是相机照下来的图片。因为它拥有完美的反射,表面也是完美的颜色,并且有生硬的影子,难看的锯齿,没有景深。我们称这种现象为失真。甚至在Kajiya(1986)形式化渲染方程之前,Cook等人就认识到渲染只是解决一组嵌套积分的过程。这些积分没有可以在有限时间内计算的解析解,所以使用蒙特卡洛解决这些问题的技巧。在cook的distributed ray tracing(1984)解决了这些问题,我们可以进行总结其特点:

  1. 使用非均匀(抖动,jittered)采样
  2. 使用噪点noise代替图像失真的情况
  3. 提供了一些特效,如Glossy reflections,Soft shadow,Motion Blur,Time等。

分布式光线追踪的主要思想是,之前我们对每一个像素进行超采样可以进行求平均值的方式来进行抗锯齿的操作,那么也可以不光只在一开始eye Ray发出多个射线,也可以在每次反射折射时也发出多条射线,这能做到比whitted光线追踪的单条反射折射射线更多的事情和效果。

最值得我们关注的问题其实是,在1986年当渲染方程被归纳出来之前,1984年的cook的论文缺乏真正推理它的数学框架,分布式光线追踪缺乏对全局照明效果的任何描述。所以你确实可以随意的发挥确保他符合LIT。

基本的直接光间接光的采样方式和path trace一样,在下一节介绍。

4.1 针对像素的采样

正如之前我们在whitted中讲到的抗锯齿的方法一样,如果没有随机性的话,不管怎样都会出现特殊的情况从而导致错误。所以,我们分布式光线追踪中使用的就是随机采样的方式(stochastic sampling),这种方法避免了像网格抽样的那种规律性。

4.1.1泊松圆盘采样(Poisson Disk Distribution)

一个采样位置不均匀分布的例子就是眼睛,眼睛有有限数量的光感受器,就像其它采样过程一样,应该是有一个奈奎斯特极限(Nyquist limit),但是眼睛正常情况下是不会发生锯齿现象的。在眼睛的中央窝(Fovea)中,六边形图案的细胞是紧密排列起来的,晶状体就扮演着低通滤波器的作用,这就避免了锯齿的发生。但是在中央窝外部,细胞的排列都很稀疏,所以采样率是很低的,然而在那里却也没有发生锯齿,原因就是通过细胞的不均匀分布来避免这个区域发生锯齿。

已经有人过研究眼睛中视锥细胞的分布,与人眼相似,光感受器在猴子眼睛的中央窝外部的分布如图所示,这样一个分布称之为泊松圆盘分布:采样点随机分布在一定的范围内且任意两个采样点之间的距离不小于某个值。距离的最小值能限制噪声的数量,举个例子,胶片颗粒(Film Grain)就是随机分布的,如下图所示,但是没有像泊松圆盘分布一样有最小距离的限制,而是采用纯随机分布。造成的结果就是一些样本点会集中一些区域而在其它的某些区域留下大量空白,所以胶片(Film)没有锯齿,有噪声(Noise)存在。

一种简单的实现泊松圆盘分布的方法是:

(1)随机生成采样位置,如果随机生成的采样位置与已经选择过的距离小于一给定值,则丢弃它,至少采样区域满为止,采用这种方法可以创建一个查询表(Lookup Table);

(2)还需要计算滤波器的值,该值描述了每个采样点与周围的像素点的关系。位置信息和滤波器的值存储在一个查询表中,这种简单的方法确实能产生很好的图像效果,但是要求有一个非常大的查询表。因此这里引入另外一种技术:抖动(Jittered)。

4.1.2抖动采样(Jitter Sampling)

抖动采样其实是分层采样(Stratified Sampling)的一种,也时随机采样的一种形式,是一种逼近泊松圆盘分布的技术。

抖动技术又有很多种类型,这里主要介绍规格网格的抖动技术,这种技术能产生较好的实验结果并且很适用于图像渲染算法。其具体原理是对于每一个像素进行分割,并对于每一块的中心区域增加一个随机的偏移,保证偏移在同一块像素里。当然这种的采样方式也可用在对区域光的采样当中。

抖动可以使得高频信号降低,但是降低的高频信号中的能量会出现在噪声中而不会消失,因此基本的光谱组合没有发生变化。与纯种的泊松圆盘分布技术相比,该技术可能会造成更多的噪声,而且可能会留下部分锯齿。

分布式光线追踪随机采样模式的一个有趣的副作用是它们实际上将噪点注入到解决方案中(略微更粗糙的图像)。 这种噪点比失真的图像更容易接受吧。

举个抖动的例子,计算时间抖动(Time Jitter)的效果,第n个样本抖动ζn的量,所以会在nT+ζn的位置采样,T表示采样周期,如下图所示,就是时间抖动的效果。可以采用不同的模型来表示抖动量ζn,比如用方差是σ2的高斯分布函数,增益量就可以为频率μ的函数,如下等式所示:

把一个像素看成是一个网格,或者由多个子像素(subpixel)网格构成的大网格,这样就是一个二维的抖动。噪声随机的加到X方向上的位置或者Y方向上的位置,X,Y方向相互独立,就相当于是两个一维的抖动构成的,要求使得每个采样点发生在某个像素网格范围内的随机位置上。如果已知道哪些采样点是可见的,则通过重构过滤器(Reconstruction Filter)对那些采样点的值进行处理。

重构过滤器的实现方法是一个开放性的问题,最简单的重构过滤器是箱式滤波器(Box Filter):取多个采样点的平均值。也可以采用加权重构滤波器,这种情况下,滤波器是一个采样位置与周围像素相关的加权值。每个像素是附近采样点的值乘以加权值的总和,这些滤波器可以提前计算好保存在一个查询表中。

抖动采样性能和方差的分析本篇文章篇幅问题暂时不予讨论,等有空的时候我们再一探究竟。


4.2区域光和软阴影

whitted光线追踪中的阴影是离散的。着色点时,检查每个光源以查看它是否可见。如果光源是可见的,则它对点的着色有贡献,否则它没有。光源本身由单个表示。


鲜花

握手

雷人

路过

鸡蛋