渲染的方法,大致有三种:
1. 基于物理渲染(Physically based):着力于模拟现实。就是说,用物理学的原理搭建关于光和物质交互的模型,追求真实感是该方法的首要任务。
2. 交互式渲染(Interactive):为了高性能而低延迟而牺牲真实感的渲染。
3. 非真实感渲染(Nonphotorealistic):为艺术的自由表达而作的渲染。
真实感渲染(photorealistic rendering) 的目标是将所生成的图像看上去和同一场景的相片难以区分。但是在大多数的情况下,我们会满足于光和物质之间物理交互的精确模拟,这要依赖于我们对能生成”好”的图像的三维显示技术的理解。
大多数真实感渲染系统(photorealistic rendering system) 都是基于光线追踪算法(ray tracing algorithm)。光线追踪实际上比较简单,光线会在场景中传播并会与其中的物体发生交互(反射/折射),并不断反弹,而光线追踪算法就是基于该光线所经过的路径。
目前有很多方法去写光线追踪器(ray tracer),所有都要去模拟以下的物体和现象:
最简单的相机就是针孔相机(pinhole camera)。针孔相机有一个一端带有针孔的暗箱。当针孔不被遮挡时,光线进入了这个小孔,打到位于暗箱另一端的感光纸上,经过足够的曝光时间而形成图像。虽然简单,但现在还有人在用呢,多半是艺术创作之用。
们就从模拟针孔相机开始:下图表示了两个锥体:一个在相机之内,另一个在相机之外,我们能够看到的景物必须在这个锥体之内,这个空间区域被称为 “可视空间”(viewing volume) 。
我们对这个相机模型做个如下图改动:把底片板放在针孔之前,而跟针孔的距离保持不变。这是一个很方便的抽象方法。我们把针孔这个位置直接称为“眼睛”(eye)位置。
放底片板的垂直平面叫 “近截面”(Near plane),相对远处的地方放一个 “远截面”(Far plane) 。这个四棱锥体就是相机所能观察到的范围。
对于一张图像上的一个像素来说,该像素的颜色值由那些沿着pinhole与成像平面上的点的方向传播的光线所贡献的(这个看第一张图)。在第二张图中,即在成像平面前置的条件下,因此,相机模型的一个重要任务就是在成像平面film上取一点A,并沿着入射光的方向(相反方向)创建一条射线。因为一条射线通常包含起始点和方向向量,这对于针孔相机来说相当简单。对于针孔相机来说,使用眼睛(eye)的位置作为起始点,眼睛到成像平面的点A的方向作为射线的方向。(需要好好理解,从眼睛的位置发射出很多射线ray,有多少个像素,就有多少条射线。)
当我们用这个相机模型生成光线时,以近截面上的点(每个点对应所生成图像上的点)为原点,以从眼睛到该点的向量为光线的方向。渲染系统的剩下部分就是去估计光线在这些射线方向上所做的贡献。第六章会更详细地讨论pbrt所用的相机模型。
相机部分:从眼睛的位置射出很多条射线,成像平面有多少个像素,就有多少条射线,即一条射线对应一个像素。
相机生成射线后,渲染器的第一个任务就是确定光线和哪一个物体先相交,以及在何处相交。这个交点就是沿着光线的可见点,这个点是我们需要计算模拟光和该物体的交互作用的地方。为了找到这个点,我们必须测试光线和场景中的所有物体的相交情况,并选择那个最先相交的那个交点。
给定一条射线 r r ,其参数形式为:
如果物体的表面由隐函数 F(x,y,z)=0 F ( x , y , z ) = 0 表示,那么光线跟表面的求交运算就很容易: 把光线方程代入隐函数中,得到关于t的方程。以球面为例:
现在假设射线与物体的交点为A,只知道交点A还不够,还要对点A进行shade运算,然而该运算需要知道该交点A的一些几何性质,例如表面法向量等。许多光线追踪器只要表面法向量就够了,但对pbrt这样更复杂的光线追踪器来说,还需要表面的关于局部的偏导数值。
当然,大多数场景中会包含许多物体。暴力求解 光线跟每个物体相交情况是很慢很慢的,时间复杂度为 O(IN) O ( I N ) 。一个优化方案是利用加速结构(acceleration structure),可以快速地排除那些不相关的物体,使得算法时间优化成 O(IlogN) O ( I l o g N ) ,其中 I I 是图像像素个数, N N 是物体个数。(建立加速结构的时间是O(N))。将在第三章描述几何求交的pbrt接口,在第四章描述加速结构。
光线和物体的求交部分:知道相机发出的射线是与场景中的哪一个物体发生相交,并知道交点。求交的过程能够利用加速结构进行优化。
在射线/物体求交后,我们得到交点上的几何信息(位置,表面法向量,等)。回忆一下,我们的目标是得到光离开交点后到达眼睛的光的能量。这需要了解光在场景中的几何分布和辐射分布。对于点光源而言,只需知道其光源位置就行了,而对于面光源(area light sources),需要一个发光的几何体来表示这个面光源。在本节,我们只讨论点光源,更全面的光分布将在第5,13章中讨论。
补充几个关于光辐射的定义:
1. 功率(power),又称为辐射通量(radiant flux),单位时间发出的辐射能。
2. 辐射照度(irradiance):单位面积上所接受的辐射通量。
3. 辐射度(radiance): 光源在发射方向相垂直的单位面积上单位立体角内的辐射通量。
辐射照度 E E 可写为: E=dϕdA E = d ϕ d A ,其中 ϕ ϕ 是辐射通量, dA d A 是包含该点的微分面积。
假设有一个包含点光源(其辐射通量为 ϕ ϕ )的半径为 r r 的球,其球面上辐射照度E是 ϕ4πr2 ϕ 4 π r 2 。对于这样的包含点光源的两个大小不同的球而言,很明显地,在大球上的点的光能要小于在小球上的光能,因为两个球面上都分布着同样的光能。这也很容易地得知,光的能量沿着光线路径以距离平方呈反比例地进行衰减。近一步地,如果微小表面片dA的法向量跟该点到光源的方向夹角是 θ θ ,那么分布在 dA d A 上的光能和 cosθ c o s θ 成比例。在 dA d A 上所分布的全部光能 dE d E (微分辐射照度:differential irradiance)是:
光的分布部分:知道光源,就可以计算出光线到达交点A处时的辐射通量,即如果我们知道光的分布,通过计算就能够得出入射光线在交点A处时的能量,还没与表面发生交互时的能量。
前面介绍的光照分布忽略了一个很重要的要素:阴影(shadow)。只有当光源到交点A的路径不被遮挡时,光源才能对该点提供照明。幸运地是,在光线追踪程序中,很容易确定光源到交点A的路径是否被遮挡:只要在该点到光源建立一条射线。这种光线叫做阴影射线(shadow ray). 我们求得这条射线跟场景中所有物体的交点中最近的那个交点B,并把交点B到被着色点A的距离跟被着色点A到光源距离比较,如果前者大于后者(或者没有交点),则不存在遮挡,因而可判定光源对被着色点有光照作用。
可见性部分:确定交点与各光源之间是否有遮挡,如果有遮挡,将不会影响交点的着色,如果没有,影响交点的着色。
为了对表面上的交点A进行正确地着色,我们已有两个重要信息: 位置和入射光的光照信息。现在我们要确定入射光如何在表面上的交点A散射的。特别是我们要知道沿着交点A到达眼睛的那部分光的能量。如下图所示:
场景中每个物体都要提供描述表面上每个点的材质信息。这个描述可由双反射分布函数BRDF(Bidirectional Reflectance Distribution Function)给出。这个函数:给定入射方向 ωi ω i 和出射方向 ωo ω o ,有多少光的能量被反射出去(沿着出射方向的能量)。记在点 p p 的BRDF函数为 fr(p,ωo,ωi) f r ( p , ω o , ω i ) 。计算散射到眼睛的光能的方法如下:
for each light:
if light is not blocked:
incident_light = light.L(point)
amount_reflected = surface.BRDF(hit_point, light_vector, eye_vector)
L += amount_reflected * incident_light
上述伪代码中, L L 表示辐射度。出射光的能量等于入射光的能量乘以BRDF,BRDF反映出物体的物理特性(有多少部分的能量用于反射,有多少部分的能量用于折射等)。BRDF的概念还能扩展为BTDF、BSDF、BSSRDF等模型。
表面散射部分:确定入射光能量中有多少的能量是用于在表面交点p处反射到眼睛的(即表面的反射)
Turner Whitted的关于光线追踪的论文强调了其递归性。比如说,如果从眼睛起始的光线碰到像镜子这样的平滑物体,我们要根据交点处的表面法向量来生成反射光,并接着对该反射光线进行递归式追踪,以找到能到达镜面上点的光源,计算出它对原相机光线的光能贡献值。同样的技术可用于穿过透明物体的透射光线。早期的光线追踪的论文热衷于炫耀那些包含镜子和玻璃球的图像,因为其它的渲染方法很难达到这些效果。注意这里讨论的是间接光照对交点A的光照贡献
一般地说,物体某点到达眼睛的光由发射光和反射光组成。这可以由光传输方程(light transport equation),又称做渲染方程(rendering equation) 来表达,渲染方程的具体公式如下所示:
积分式是指在包含点P的球体上对所有方向的入射光所产生的出射光的贡献值的积分,它用到了BRDF函数和cosine值。
求这个积分的解析值是很困难的,我们必须简化前提或者用数值积分来求解。Whitted的算法忽略了绝大部分方向的入射光线,指对点到光源的方向和全反射和全折射方向上求Li(P, ωi)。换句话说,它把积分转换成对极少数方向上的累加。
我们可以扩展Whitted算法,使其不仅模拟镜面全反射和玻璃。例如,在靠近镜面反射的方向上递归地追踪许多光线,并对它们的结果取平均值,这样我们就得到粗糙表面的反射效果。第14和16章有更详细的解释。当我们递归地追踪光线时,我们就在每个图像点上建立了一颗光线树。从眼睛发出的光线就是这棵树的根。树上每条光线都有一个权值与之联系,这可以让我们模拟那些光滑的但并不100%地反射入射光的表面。
间接光传播部分:前面部分讨论的是光源直接对交点A的着色影响,而间接光传播部分讨论的是光源间接对交点A的着色影响,即来自于其他物体的反射光对该交点的影响,眼睛到交点的方向称为观察方向,由观察方向和交点表面法向量计算得出反射方向 R⃗ R → ,其他物体的反射光的方向与 R⃗ R → 相同(方向取反后相同)的话,那么这些其他物体的反射光就会对该交点产生影响,因此从交点处向方向 R⃗ R → 发射一条射线,寻找与另一个物体的交点,递归上述的过程,如果找到的是光源,那么该射线的路径上的物体对原始交点A都会产生影响,如果找到的是其他物体,则不会产生影响。
前面的讨论是假定光在真空中作不衰减地传播,当环境中有雾,烟或灰尘等现象时,就不适用了,而且只要在户外,光线肯定受地球大气的影响。
参与介质(participating media)有两种方式影响光的传播。
- 首先,参与介质可以通过对光的吸收和散射来消弱光的强度。我们可以计算光源和交点A之间的透射比(transmittance)T来模拟这个效果。
- 另外,参与介质还可以加强光的强度,比如参与介质本身发光(火焰等),或者参与介质从其他的方向传来的光散射到光线的方向去,这就要求体积光线传输方程(volume light transport equation)(见第12章和第17章)。
光的传播部分:该部分考虑了光线在参与介质(通常是空气、烟雾等环境)中传播时能量的变化(增强还是减弱),这样入射光线的能量就会随着参与介质的不同而发生变化。
基于上述1-7部分得出的思路:
1. 首先,从眼睛处发射出像素个数射线。
2. 求出各射线与场景中的交点A,以及获取交点A处的几何信息。
3. 获取各光源在空间中的分布,并求出各光源在交点A处的入射光的能量。(直接光照影响)
4. 检查各光源与交点A之间是否存在遮挡,如果存在遮挡,忽略该光源的影响,如果不存在,考虑该光源的影响。
5. 知道入射光方向和观察方向,使用BRDF函数求出入射光中有多少能量是用于方向为观察方向的反射光,该部分的能量影响交点的着色。
6. 知道观察方向和表面法向量,求出间接光照对该交点的影响(间接光照影响)
7. 考虑光线在参与介质中能量增强或减弱的情况。