光线追踪(二) 辐射度量,BRDF,渲染方程,蒙特卡洛路径追踪
基于物理渲染的基础知识,包括辐射度量学,BRDF和渲染方程
为什么需要辐射度量学
首先,想一想到目前为止我们对光线亮度的定义是什么?只是简单的用3维(R,G,B)向量来描述,但却没有任何的单位,如果想要一个完全正确的模型,这显然是不应该的,必须用合适的物理量来描述光线。
其次,考虑whited-style光线追踪模型,它真的是一个正确的模型吗?显然不是,原因如下:
1 whited-style光线追踪并没有对漫反射的光线进行追踪,而是直接返回当前着色点颜色
2 在计算光源直接照射的贡献时,使用了Blinn-Phong模型,而Blinn-Phong模型本身就是一个不准确的经验模型,使用的这种模型的whited-style光线追踪自身自然也是不正确的
辐射度量学概念
辐射度量学其实是对光照的一套测量系统和单位,它能够准确的描述光线的物理性质。
具体来说,我们需要明白的是其中的几个关于光线的概念,分别为: 辐射能量(Radiant energy),辐射通量(Radiant flux),辐射强度(Radiant intensity),irradiance,radiance,(对后两种概念没有合适的中文,所以就直接用英文了),接下来就对这些概念进行具体解释。
辐射能量(Radiant energy)和辐射通量(Radiant flux)
辐射能量
辐射能量其实非常直观,就是辐射出来的电磁能量,单位为焦耳。可以用物理当中的做功的大小来进行类比。
辐射通量
所谓辐射通量或者说辐射功率,其实就是在辐射能量的基础之上除以时间,也就是单位时间的能量。同样也可以用物理当中的功率来进行类比。
(tips: 具体来说一般偏向用radiant flux来衡量光线的亮度,因为我们更关心的是单位时间的效果,事实上也是这么做的,想想在说白炽灯泡的时候也是说60W亮度,80W亮度)
辐射强度(Radiant intensity)
1 Radiant itensity其实就是指从一个光源出发某一方向上的亮度
2 Irradiance指某一微小平面所接受到的光线亮度
3 radiance衡量的是一条传播光线所具有的亮度(不受传播方向影响而改变)
Radiant itensity
Radiant intensity一句话来说就是从光源发出的每单位立体角上的功率,关于辐射功率的定义在上文已经解释,这里唯一还不知道的就是立体角(solid angle)了。
solid angle其实就是对应二维空间中圆的弧度在三维空间中球上的拓展。 首先看在二维计算弧度公式如下:
那么对应在三维上的球的弧度(立体角),只需进行一个简单的扩展如下:
那么对于Radiant intensity的定义当中,微分立体角dw计算如下:
推导过程:[https://zhuanlan.zhihu.com/p/450731138]
irradiance
irradiance是指每单位照射面积所接收到的power,单位如图中所示。 借助于irradiance,可以很轻松的解释在Blinn-Phong所提到的Lambert’s Law,即光线亮度在计算时需要乘上一个cos,如下图所示:
此外,回想一下也是在Blinn-Phong模型所提到的光线越远会越加衰减:
该现象也完全可以用irradiance解释,因为光的功率始终一致,离点光源所照射到的圆球面积也就越大,因此根据irradiance的式子,分母的面积值也就越大,irradiance也就越小。
radiance
用一句话概述的话,所谓radiance就是指每单位立体角,每单位垂直面积的功率,直观来看的话,很像是Intensity和irradiance的结合。它同时指定了光的方向与照射到的表面所接受到的亮度。
但这里有一个细微的区别,在irradiance中定义的每单位照射面积,而在radiance当中,为了更好的使其成为描述一条光线传播中的亮度,且在传播过程当中大小不随方向改变,所以在定义中关于接收面积的部分是每单位垂直面积,具体可以观察如下图:
=>
BRDF(双向反射分布函数)
通过上述所有辐射度量学各种概念的定义之后,我们可以从这样一个角度理解光线的反射,如下图所示:
反射方程
借助BRDF,可以得到反射方程如下:
到这里,通过辐射度量学,以及BRDF最终得到的反射方程正是一个完全正确的光线传播模型了,解决了在第一章提到的现有模型的所有缺点!(渲染方程只是在反射方程的基础之上加了一个自发光项)
入射光线的radiance不仅仅是光源所引起的,还有可能是其他物体上着色点的反射光线的radiance,恰好反射到当前的着色点p(即间接光照),同时其他物体上的反射光线的radiance依然也是由直接光照和间接光照构成,因此这与whitted-style当中的光线追踪过程十分类似,也是一个递归的过程。所以说想要解这样一个方程还是比较难的
渲染方程及其物理含义解释
不同情况:
一个点光源,单个物体:
(点光源对一个点来说自然只有一个方向有入射光,所以这里没有了积分)
多个点光源,单个物体
将这些所有的点光源的贡献全部求和即可,那么如果点光源变成了面光源呢?如下图所示:
其实面光源就相当于无穷多个点光源的集合,只需要对 面光源所在的立体角范围进行积分,并且能够确定不同立体角方向的面光源的入射光radiance即可。
那么更进一步的,再在场景当中加入其它物体,使得物体之间发生光线交互之后是什么情况呢:
如上图所示,可以把其它物体同样考虑成面光源,对其所占立体角进行积分即可,只不过对其它物体的立体角积分不像是面光源所有入射方向都有radiance,物体的立体角可能只有个别几个方向有入射的radiance(即多次物体间光线反射之后恰好照射到着色点x),其它方向没有,但本质上都可以视作是面光源。
这里就类似用了递归思想了
观察一下图中的渲染方程可以发现除了两个radiance,其它所有项都是知道的,可以将上式进一步写成如下图下方所示的式子:
其中各项与原渲染方程中一一对应,(这里其实是有数学严格推导的,不过我们只是为了接下来构建直观的物理解释,对于这些推导不必在意,默认成立即可),再接着,可以把该式子离散化写为线性代数的形式:
仔细观察这个式子,注意E是光源所发出的光,K为反射算子,这样一个式子的物理含义如下图所示:
蒙特卡洛路径追踪
如何解渲染方程? 利用蒙特卡洛路径追踪来完成这个目标。
蒙特卡洛积分
蒙特卡洛积分的目的: 当一个积分很难通过解析的方式得到答案的时候可以通过蒙特卡洛的方式近似得到积分结果,如下图所示:
显然对于这样一个函数,很难去用一个数学式子去表示,因此无法用一般解析的方法直接求得积分值,而这时候就可以采用蒙特卡洛的思想了。
蒙特卡洛积分的原理及做法: 对函数值进行多次采样求均值作为积分值的近似
该做法十分容易理解,想象一下如果对上图这个函数值进行均匀采样的话,其实就相当于将整个积分面积切成了许许多多个长方形,然后将这些小长方形的面积全部加起来。没错,该做法其实就与黎曼积分的想法几乎一致。但蒙特卡洛积分更加的general,因为它可以指定一个分布来对被积分的值进行采样,定义如下:
但在本文中为了方便,所有的采样都使用均匀采样,因此很容易推出:
因此,蒙特卡洛在此来说就是一个帮助求得困难积分值的方法。
蒙特卡洛路径追踪
在进入具体计算之前,对渲染方程做出一点小修改,即舍弃一下自发光项(因为除了光源其他物体不会发光), 以方便进行计算推导:
从具体例子出发,首先仅仅考虑直接光照:
再次观察该修改过之后的方程其实就只是一个单纯的积分计算
,其物理含义为着色点p到摄像机或人眼的Radiance值。
此时的计算伪代码如下:
显而易见的,单独仅仅考虑直接光照自然是不够的,还需要间接光照,即当采样的wi方向碰撞到了别的物体,如下图所示:
此时采样的光线碰撞到了另一个物体的Q点,那么该条路径对着色点P的贡献是多少呢?自然是在点Q的直接光照再乘上反射到该方向上的百分比了!显然这是一个类似光线追踪的递归过程,不同在于该方法通过对光线方向的采样从而找出一条条可行的路径,这也正是为什么叫路径追踪的原因,伪代码如下:
至此,我们成功通过蒙特卡洛的方式解出了渲染方程的积分值,也通过考虑直接光照与间接光照解决了递归的问题。但该方法至此有一个非常致命的缺陷:
我们通过每次对光线方向的采样从而解出方程,假设每次采样100条,那么从人眼出发的第一次采样就是100条,在进行第二次反射之后就是10000条,依次类推,反射越多次光线数量便会爆炸增长,计算量会无法负担,那么如何才能使得光线数量不爆炸增长呢?唯有每次只采样一个方向!N=1
每次如果只采样一个方向那么所带来的问题也是显而易见的,积分计算的结果会非常的noisy,虽然蒙特卡洛积分是无偏估计,但样本越少显然偏差越大。但该问题很好解决,如果每次只去寻找一条路径结果不好,那么重复多次寻找到多条路径,将多条路径的结果求得平均即可!如下图所示:
改良之后的Path Tracing伪代码如下:
通过对经过像素的光线重复采样,每次在反射的时候只按分布随机选取一个方向,解决了只对经过像素的光线采样一次,而对反射光线按分布采样多次所导致的光线爆炸问题。
那么现在所有的问题都解决了吗?还没有!因为shade函数的递归没有出口,永远不会停下。 但这里并不没有采用类似光线追踪当中设定反射深度显示的给出递归出口的方法,而是非常精妙的采用了**俄罗斯轮盘赌(Russian Roulette)**。
shade函数的伪代码变更如下,使得可以停止递归了:
至此,我们的路径追踪算法已经完成大半,只差最后一个小问题!现在的路径追踪效率非常的低下,如图所示:
pdf:概率密度函数
tips:计算直接光照的时候还需要判断光源与着色点之间是否有物体遮挡,该做法也很简单,只需从着色点x向光源采样点x’发出一条检测光线判断是否与光源之外的物体相交即可,如图所示: