JoshWindsor 发表于 2022-8-15 17:25

Unity-面试篇:PBR物理光照计算公式推导详解

前言



对惹,这里有一个游戏开发交流小组,希望大家可以点击进来一起交流一下开发经验呀


(5)漫反射:光线被粗糙表面无规则地向各个方向反射;
(6)镜面反射or高光反射:镜面反射是指若反射面比较光滑,当平行入射的光线射到这个反射面时,仍会平行地向一个方向反射出来;


(7)双向反射分布函数BRFD:给一个入射角度和一个观察角度,它能给出一个决定最终射向观察角度的光的强度的系数;
(8)可见性函数V:描述光线从入射到出射的过程中,有多少比例被微表面自身的凹凸不平遮挡主了;(9)法线分布函数D:用来描述组成表面一点的所有微表面的法线分布概率,如今的PBR时代,业界主流的法线分布函数已从传统的Blinn-Phong等分布,迁移到更接近真实材质外观表现,具备能量守恒,具有更宽尾部和更高峰值的GGX分布,而且在朝着多高光波瓣(multiple specular lobes)的方向发展。(10):基于经验的光照与基于物理的光照:基于经验的光照计算是指根据观察与经验得到的着色公式如Blinn-Phong,基于物理光照是基于物理理论的光照计算(PBR),基于经验的公式是基于物理光照公式的某一种特殊的形势。(11)菲涅尔现象F:在一个反射面上,当入射角度越“倾斜”,反射的光线越多,折射的光线越少,入射角度越“直”,折射的光线越多,反射的越少。
具体公式推导我们先从经验光照模型的Blinn-Phong开始,然后再逐步的演变推导到物理PBR公式。Blinn-Phong光照计算的公式如下:


这个公式表明:光照计算=漫反射+高光反射。
Cdiff:为漫反射的颜色;Cspec:为高光颜色,或者叫镜面反射的颜色N.L是表面法线和光线方向的点乘,又是两个向量的余弦,下面称cos θLN.H是表面法线和半角向量的点乘,也是两个向量的余弦,下文称cos θh,半角向量为视线向量和光线向量的中间向量,计算方式是单位化(L + V)向量m控制高光大小范围,m越大,光斑越集中,越小时,光斑越分散。我们把Blinn-Phong公式调整替换一下,如图:


这个公式GPU计算起来简单,性能比较好,但是效果不好,所以接下来,我们来对某些参数来做一些调整,如图:



注意看这个公式前后两大变化:
a:漫反射系数改变了,增加了一个1/PI的系数,它会让我们在给光源设置Diffuse的时候更加直观,高光系数改变,增加一个系数(m+8)/8*PI,这样会导致原公式中m越大高光的光斑越集中,带上这些参数后,m越大,不仅让光斑越集中,还能让高光更亮。我们来对比一下前后变化,如图:



b:提取了公因式cos θL, 这个更合理,所有光线照射的效果都需要考虑受光面倾斜度,只是老公式认为在高光处这个影响不明显,所以省略掉以节省性能。提取完公因式以后,我们把括号里的部分当作整体,这就是著名的BRDF函数。称“双向反射分布函数”。于是我们的公式演变如下:



我们来看下基于BRDF结构下的Blinn-Phong光照计算公式:

第一部分Cdiff/PI:是一个Lambert光照公式,用来计算Diffuse;第二部分是高光部分,用来计算高光反射:



这个公式的作用是控制高光产生的光斑的集中度。以一个球体为例,越接近光斑中心,半角向量和法线的夹角越小,高光越亮,越到光斑外围,半角向量和法线的夹角越大,高光越弱。听上去挺合理,但翻开物理课本回顾一下就会发现:理论上只有半角向量和法线完全重合的时候才有反射光线被视线接收到,凭什么这里还能允许它们存在夹角?而且夹角的变化还导致反射光线强度的变化,这个怎么来的?这里又有新理论,叫微表面理论。我们在渲染中是逐个像素进行处理的,每个像素虽然足够小了,但在这个像素覆盖的范围内还有无数细微的起起伏伏的物理结构,有起伏,就会有变化的法线。我们渲染时用到那个像素法线只能算是所有细微面的法线的平均值,实际上这个像素点内部朝各个方向的法线都是有的,只是所占比例有所不同。



在渲染时,当然不会把像素内部的法线逐个取出来再去计算光照,我们只需要把能反射到视线的法线所占的比例找出来,用它将高光强度修正一下就好了。其实我们常说的“半角向量”就是指能反射到视线的法线。而我们讨论的 高光部分的公式就是做这个事,它根据半角向量和平均法线的夹角,计算出指向半角向量方向的微表面法线的比例。这个函数叫做 “法线分布函数”,文中我们以D来表示。然后我们的BRDF函数就演变成这样:


法线分布函数能解释表面上细微的凹凸不平对光的反射产生的影响,但是有一个前提:微表面上所有的点都能够接受到光线,并且能够反射出光线,事实上真实的物理表面大部分是达不到这样的条件的,如图所示:


由于自身的遮挡,有很多点根本无法接受到入射光线,也有很多点反射的光线无法射出表面。因此,我们此处再定义一个函数叫“可见性函数”,用V表示。它的作用就是根据给定的入射光线和出射光线的方向,计算出不被自身遮挡的光线的比例,上面的BRDF公式就演变成如图所示:


高光强度Cspec,在公式中,这是个常量,但却不是符合真实物理规律的。在物理书讲反射与折射的章节上我们学过,在一个反射面上,当入射角度越“倾斜”,反射的光线越多,折射的光线越少,入射角度越“直”,折射的光线越多,反射的越少。这个现象叫做菲涅尔现象。所以这里的高光强度需要用一个函数来表示,记作F。这样基于经验模型的Blinn-Phong光照公式被调整成有物理意义的公式模板,如图:


尽管如此,从物理公式的角度我们来分析一下Blinn-Phong光照之所以效果不好的原因:(1)法线分布函数D的自变量基于一个没有太大物理意义的m(2)菲涅尔函数F是高光常量,表示完全没有考虑入射光线角度不同带来的高光变化;(3)可见性函数V是常量1,表示所有微表面上的光线完全没有自遮挡;(4)漫反射与高光之间独立,表示没有考虑光的能量守恒;
为了提升渲染效果, PBR根据物理的理论引入了一些公式,如下:
法线分布函数D:找一个更符合物理规律并且更容易控制的函数来代替原有的法线分布函数。在各种常见的PBR模型中被广泛使用的是Trowbridge-Reitz GGX函数:


其中,Roughness就是我们常说的“粗糙度”,取值范围为0到1,当取值为1时表示最粗糙的表面,完全没有任何高光反射。这一点可以从公式中看出来(注:有的系统例如Unitiy里用“光滑度”来代替粗糙度,它们的关系是:粗糙度= 1 -光滑度)。
菲涅尔系数F:当光垂直射向表面时,折射光线最多,反射光线最少,假设此时的菲涅尔系数是F0,那么在给定任意角度的时候,引入下面公式来近似的表示这个系数


其中F0是在限定条件下折射和反射的比例,由物质的分子结构决定。是物质的天然属性。每一种物质都不相同。用一个更常见的说法,它叫做“反射率”。通常通一种物质对不同频谱的光反射率是不一样的,所以不管是F还F0是都是一组RGB三通道的值。它的值到底是多少呢?先看一组通过真实物理实验得到的数据:水: (0.02, 0.02, 0.02)塑料: (0.03, 0.03, 0.03)玻璃: (0.02, 0.02, 0.02)金: (1.00, 0.71, 0.29)铝: (0.91, 0.92, 0.92)…我们可以看到,非金属的反射率都非常小,而金属的反射率则比较高,大部分在0.5以上,RGB三个通道的差异体现了这种金属的颜色,值越大则金属越“亮”。
因此,对于非金属,我们其实可以用统一的一个比较小的值作为F0(例如Unity引擎里将这个值定为常数(0.04, 0.04, 0.04)),对于金属,我们以金属自身颜色Albedo作为它的值。理论上一种物质要么是金属,要么是非金属,不会出现介于金属和非金属之间的状态。但实际我们做3D渲染的时候,一个像素面积内是可能同时包含金属和非金属的。为了准确描述这种现象,我们定义一个值叫做“金属度”,当它为1时,反射率完全按金属的方式处理,为0时,完全按非金属的方式处理。当取值在0和1之间时,通过线性插值确定反射率,如下:


可见性函数V:可见性函数是描述光线从入射到出射的过程中,有多少比例被微表面自身的凹凸不平遮挡主了。这个部分其实和法线分布有点像,依据的是表面的粗糙程度。为了准确描述,我们首先需要这样的一个函数:给一个方向,获得微表面在这个方向上被自遮挡的面积的比例。引入Schlick-GGX函数公式:


公式中的k其实是对粗糙度的一个简单变形,在只考虑一个固定方向的情况下,这个变形的公式为:


我们最终计算的可见性分了三部分:入射光线没被遮挡的部分、出射光线没被遮挡的部分、入射光线被遮挡后,在微表面内部多次反射又射出的部分。我们省略掉推导过程后,得到的公式是这样:


这就是我们的可见性函数了。BRDF公式也就演变成:


其中V、D、F三个部分我们都已经给出了物理公式,最后就是考虑能量守恒了。
光照能量守恒:我们通常会把光照效果分为漫反射和镜面反射(既高光)两部分。传统意义上说的“漫反射”是指凹凸不平的表面反射的没有方向性的光。但是我们已经在高光部分充分考虑了物体微表面的变化了啊。而且事实上我们把粗糙度调整为1时,效果跟我们预期的“漫反射”就是一样的。


而现在BRDF公式中的Diffuse实质上是这样的:光线射到表面上后,部分被反射,部分被折射而进入材质内部,在材质内部经过多次不规则的折射、反射后,部分光线被吸收,部分光线又从离射入点不远的地方射出来,这里的“不远”是指映射到屏幕后小于一个像素的大小。所以反射率越高,则越多的光线被直接反射,越少的光线进入材质内部,Diffuse就越低。反之亦然。所以我们还需要两个系数来修正Specular和Diffuse两部分,分别称作Kspec和Kdiff,如下:


我们根据能量守恒的原则可知,Diffuse、高光能量之和一定小于或等于入射光的总能量,少的那部分是被吸收的部分,从前文的分析我们还可以肯定被吸收的部分一定是在被折射的光线中扣除的。从物理性质上来说,金属几乎吸收全量的折射光线,非金属则不怎么吸收光线。根据这样过的几个性质,我们得到这样的近似的关系:
首先高光系数其实就是前文所描的反射率F0


然后,Diffuse系数则是剩余部分扣除吸收的光线,而吸收的光线比例通过金属度来近似模拟:


这样整个PBR的公式的由来就大体的全部讲解了一遍,写Shader的时候,只要对着公式的模板来整理出来不同Shader描述不同函数的公式,这样看懂它最终PBR Shader的写法。
附:视频教程
页: [1]
查看完整版本: Unity-面试篇:PBR物理光照计算公式推导详解