//====================================
打一个标记在这里,因为最近看了Games202的课程,有关BRDF的部分希望进行一些补充就不单独开新的部分了。要想了解Games202课程关于PBR的笔记,推荐一个博主的文章(GAMES202 笔记 -Real-Time Physically-Based Materials_奇迹小缘的博客-CSDN博客),我个人觉得很全,不过可能还是不好理解,给我的感觉就是将老师上课的东西直接摘下来了,怎么理解没有详细说说,现在我要加上我自己的理解,也欢迎大佬指正。
//====================================
本篇专项讲解基于物理的渲染PBR(PBS),到底最后一个所学是Rendering还是Shadering并不重要,因为指代的都是一个东西。
我也是在对了比好多资料之后给出本篇的写作内容,下边就直接开始吧。
一、PBR简介
基于真实世界物理光线与材质界面交互过程建立起来的渲染理论。
PBR的特色就是深度还原一束光,从发出到达材质界面,然后分为反射与折射(可能会再次射出进行漫反射),呈现最终综合的光照渲染结果。
二、必要条件
要确定为PBR,需要满足以下三个条件:
1.基于微表面模型;
2.能量守恒;
3.使用基于物理的BRDF;
从某种程度上来说,使用基于物理的BRDF也就满足了前两条,因为基于物理的BRDF就是建立在前两条基础上推导、模拟出的渲染方程。
三、微表面模型
鸿篇巨制有很多,但我倾向于用最简短最白话的叙述来表述我的观点。
所谓微表面就是相对我们肉眼可见的宏观表面提出的,它能反映材质微小表面的粗糙起伏情况(法线的不规律分布)。在这个部分,我们只做一件事:
使用表面粗糙度估算半角向量与宏观法向量的偏离程度(一致性概率)。
四、能量守恒
出射光能量不能大于入射光总能量(自发光除外)。
这就是我们要表达的能量守恒观点,具体解释一下。光与界面接触后,分为反射光与折射光两个部分,其中反射光直接离开表面形成高光,折射光进入材质内部,与内部电子等作用产生能量转化,可能会有部分光再次射出材质表面,成为漫反射部分。也就是说漫反射实际是折射光的一部分(金属吸收所有折射光,所以没有漫反射)。因此:
总能量=反射光能量+折射光能量=反射光能量+(漫反射能量+)材质吸收能量。
通常情况下PBR会结社折射能量被完全吸收,不再进行漫反射。更高级的次表面反射确模拟了光重新离开材质,但性能开销较大。
五、反射方程(渲染方程)
这是本文的重点,我们也可以从中找到与前边的对应关系。
先直接看一下这个方程的表达式:
先拆解这个公式
表示BRDF,双向反射分布函数
方向
上的入射光在p点的光照贡献(总光能量)
对于第二部分总光能量,有很多资料讲得很模糊或者又太深奥,会涉及很多光学理论,又是积分又是微分啥的,对初学者并不友好。当然如果能从根源上理解这个问题当然是极好的,不过我还是想表达我简单粗暴地观点,这个地方我的理解就是:
光从光源发出后,经过衰减与角度投射后到达观察表面p的光能量,具体的内容可以参考我在专栏文章《D3D12学习笔记——入射光》对入射光衰减的表述。
所以实际就是总光能量,考虑了衰减与投射之后真实到达材质界面的总能量。
现在来看比较复杂的第一项BRDF,也是本篇的重点。
BRDF
双向反射分布函数,是一个计算高光反射+漫反射分别作用于总光能量占比的函数(加权值)。
还是给出其表达:
包含了两个部分,漫反射与高光反射,漫反射部分比较简单:为折射占比,表示入射总光能量被折射的权重值,对应的
为反射光占总光能量权重,根据能量守恒:
对于表示折射光部分参与漫反射的比率,换句话说就是对总光能量的二次折减:
漫反射=总光能量X折射占比X漫反射占比
其表达如下:使用π是因为后边还有积分。
表示材质的漫反射颜色(Albedo),也可以形象的理解为材质表面颜色(定义的对RGB的吸收比率)。
这个部分使用也就与我们之前基于勃朗特的漫反射模型对应起来了:
可以看出如果把积分考虑进来,传统的兰伯特也还是有可取之处的,唯一的差异性就是对系数的忽视,也就是说传统兰伯特模型并不是能量守恒的。
解决了漫反射项,我们再来看看最为复杂的高光反射项,这个地方实际有很多种实现,这里我们解释Cook-Torrance的高光项,因为这在实时渲染管线中使用最广。
分母部分相信没啥好说的,法线n,入射方向wi和出射方向wo.
重点看看分子部分:
D:法线分布函数
GGX模型
这也是我们使用微表面模型的原因,即使用表面粗糙度估算半角向量与宏观法向量的偏离程度(一致性概率)。这正是我们使用微表面的目的,具体的计算方法如下:
roughness是0-1区间的数,越小表示越光滑;
h为半角向量;
n为法向量;
他的一个性质就是long tail长尾效果,就是从中心到边界是渐变的,会有一个光晕的效果:
//=============
来自Games202
Beckmann模型
只有两个变量,粗糙度(roughness)和角度
(宏观法线方向与半程向量的夹角)
GTR模型
Generalized Trowbridge Reitz是对GGX的拓展,下边是他的表达式,看上去是很简洁的,并且当比较大的时候能很好的拟合Beckmann模型,但是最关键的一个问题是分子c,我查了资料,这个c还是由粗糙度
(roughness)和角度
(宏观法线方向与半程向量的夹角)表达,但是一直没找到统一表达,这可能就是他比较难使用的原因。
F:菲涅尔项
我在专栏文章《D3D12学习笔记——光照》部分介绍过这个:
这里要注意一下,我看到了很多不同的版本,主要在于,这里使用的是法线与视向量,也有使用半角向量与视向量
,也有使用法线与光向量
,因为这本身就是一个近似(石里克近似),所以我觉得差异不大,应该来说都是可以的,但是这项本身的定义是:不同观察方向上,反射光比率不同。因此使用视向量与法向量是比较合理的。
G:几何遮挡
这个函数用于模拟不平整微表面的相互遮挡,主要是指1.对视线的遮挡;2.对光线的遮挡。这两点的基本思路是一样的,因为都把视线v和光线L当做向量来处理,处理的基本表达式为:
其中d可以表示视线v或者光线L,表述微表面对视线或者光线的遮挡导致的阴影占比。那么要同时考虑视线与光线,则要进行整合(乘积):
遮挡视线:
遮挡光线:
则最终的几何遮挡函数:
上边的式子中:n表示表面法线,v为视线向量,L为光线向量,对于k:
适用于直接光照
适用于光照贴图(来源于场景烘焙等)
//=============
来自Games202
使用不同的NDF对G会有一定的影响,因为N的预测实际也就是微表面朝向预测,对于光线和视线的遮挡就会有影响,以下是差异:
但是无论如何都是满足基本观察规律的,那就是角度为0的时候,此时半程向量与法向量一致,相当于没有遮挡,结果都是1。
来自Games202
Missing Energy
这个部分Games202讲解比较难理解,我看的笔记更多的像是课堂记录,没有个人理解,我想分享一下自己的理解,希望有大神帮我指正。
我们常说能量守恒,到目前为止,看上去我们这个过程就是能量守恒的,其实不然,因为我们考虑G的时候是决定光线不被遮挡,视线也不被遮挡的占比,来决定光线的出射。但是实际上,光线经过多次反射还是会射出来的,这就会导致能量不守恒,使得如果表面粗糙的时候颜色会变暗。
先解释基本思路:
既然是因为遮挡导致多次反射,这部分能量只使用G会被丢失,导致BRDF计算值偏小,那么就想办法把他加回来。这个办法课程介绍叫做Kulla-Conty。
方程如下:
解释:
1).使用入射光照总能量为1,(常数1,无论乘积还是积分都不会导致结果有误,因为我们只是算一个比例)
2).积分式计算的就是对渲染方程(无光照,仅考虑BRDF在整个半球面的积分结果),也就是说计算了对整个半球面来说光的出射比例;
3).既然积分式计算的是出射比例,换句话讲就是要参与后续反射的能量比例,且由于光路是可逆的,因此真正被遮挡,要参加后续反射的能量占比为:
c为归一化的系数(可以是常量和函数)
4).计算能量损失的BRDF
注意分母的平均,对他的求解是使用split sum一种打表的方式:这个二重积分只需要一个二维表(粗糙度与角度)
以上讨论建立在初始光照能量为1的情况,并且没有颜色值,如果有颜色值那么颜色本身就是一种吸收(损失),那么我们不妨换个思路来考虑:将每次反射出来的能量进行累加,因为要进行累加,并且弹射角度很多,所以使用平均才有意义:
1.直接能看到的能量:color*Favg * Euo;
2.弹射一次能看到的能量:color*Favg * (1-Euo) * FavgEavg;
.....
3.弹射k次能看到的能量:color*Favg ^k* (1-Euo))^k * FavgEavg;
那么所有看到能量项求和
其中:因为我们使用的是总能量为1,所以这个数值位于0-1之间,真正表示了出射能量的占比,也就是真正的BRDF,而不是简单的基于G一次遮挡的考量。
这里边我们要计算两个平均值:
1.一个是出射能量的平均值,上边说了使用打表的方法;
2.菲涅尔项的平均值:不管入射角多大,平均每次反射会有多少能量反射。平均菲涅尔项的计算就是计算在所有角度下,菲涅尔项的平均值:
因此,我们考虑Miss Energy的正确做法,或者叫做光线经过多次弹射(而非一次的G遮挡)的计算方式,就是计算和
,用来估算最终多次弹射出来的能量占比,这才是真正的BRDF项,他将DFG考虑进平均值里边,在满足求极限得到真正的BRDF值。但是计算效率肯定是不太好的,所以了解即可。
我也不知道我理解得是否准确,欢迎大佬来指正,并且如果我错了,我后边会修改。这里要注意一件事,Games202中闫老师着重批评了一件事就是使用一个diffuse来填补能量损失的部分,认为这是完全错误的。确实,从物理的角度来说真的是错误的,但是这样很快,所以很多地方给出了下边的方程:
要注意ks表示反射光的占比,而F实际计算就是针对反射光占比(考虑了不同观察角度),因此这里不需要单独乘ks了,正确的表达为:
注意为入射光沿方向
投射到材质界面的总光能量。也就是说我们计算的漫反射与高光反射实际都是在计算权重占比,是基于物理材质的结果占比。
实际上,这种强加diffuse保持能量守恒是不正确的,会导致产生一些发光的brdf(结果大于1),但是它快啊。换句话说,如果你使用平均反射能量与平均菲涅尔计算能量损失,就不必按照上式强行补上diffuse,如果只考虑一次G,那么上式虽然不是物理正确的,但是起码更加能量守恒。一个好的思路是让diffuse项成为自适应的,既简单又满足能量守恒,希望以后可以出现。
补充Games202考虑可见性的渲染方程:
注意看这里的和
具有相同的自变量,实际在直接光照中他就是合在一起的。在Games202中考虑全局光照,实时渲染关节分开来看(实际光照+是否可见),两项的综合有利于我们区分精确光源直接光照和环境光照的影响(贡献)。
OK,以上就是我对于BPR的认识,当然如果你接触过基于物理的引擎,比如Unreal,Unity等,就会明白我们在对材质进行建模时大体指定一些描述材质的参数,而这实际是Disney's principle BRDF,这部分也可以在Games202课程的后半程找到,他虽然不是物理正确的,但是对于设计者来说非常友好,但到底层应该也是为了用于我们上述方程的输入。