1 BRDF
在上一篇文章当中,我们谈到了BRDF公式,它是用来计算一个光源对物体表面的影响结果,在图形学中,这个结果我们通常用RGBA颜色来表示。这一节,将展开谈谈BRDF。
BRDF帮助我们计算出:一条光线 打到物体表面一点上,应该成什么颜色。
- BRDF,Bidirectional Reflective Distribution Function,双向反射分布函数
镜面反射与漫反射¶
根据物理定律,我们知道当一束入射光(incident light)打在一个表面上时,将分成两部分:镜面反射和漫反射。
它们还有很多别名:
- 镜面反射:specular reflection、高光反射
- 漫反射:diffuse reflection、折射+散射
镜面反射¶
根据光的反射定律,当一束光碰撞到物体表面上时,它的一部分能量会沿着与平面法向量的对称方向反射出去,而不进入物体,这一部分即被称为“镜面反射”。
如下图Specular highlights箭头所指区域,即是“镜面反射”的效果
- 白色:由于入射光是白光,而“镜面反射”是光线撞到表面后,直接反射出去的,因此“镜面反射”也是白光。
- 亮亮的:镜面反射光的方向一般都集中在上图的\(B\)方向的周围,因此看起来亮亮的。这与漫反射不同,漫反射的光线是四面八方的,它的能量被均摊到四面八方了。
镜面反射与表面的粗糙度有关
- 如果表面越粗糙,下图中\(f_r\)区域就越大。那意味着,在物体表面上高光区域很大,但强度较弱
- 如果表面越光滑,下图中的\(f_r\)区域就越窄,直到反射光全集中在镜面方向(入射光轴对称的方向),这意味着,在物体表面上高光区域很小,但强度非常大,非常刺眼
漫反射¶
根据物理定律,我们有以下认识(如下图中的橘色光线)
- 当一束光碰撞到表面上时,它的一部分能量会折射进物体内部
- 物体由无数微小的粒子所组成,光线进入物体内部之后,会与这些粒子再次发生碰撞,在粒子间“弹来弹去”(即在物体内部发生 散射)
- 在碰撞过程中,粒子会吸收光线的一部分能量,然后转为热量
- 若在传播过程中,光线的某条分支能量耗尽,光线就出不去了,全被吸收转换为了热量
- 若在传播过程中,光线能量未能被全部吸收,就成功离开表面,构成所谓的“漫反射光“,它们将协同构成物体表面的颜色(只是漫反射部分的颜色)
漫反射:经过折射、散射之后,还能从物体内部射出表面的光线。它们的方向随机,但各个方向的能量可以假定为相同。
- 如下图,Diffuse reflection箭头所值即是漫反射作用下产生的颜色
- 漫反射其实是物体的主要颜色,从上面交代的物理原理可知,它是物体的主要颜色,也直接取决于物体的材质。比如某个红色衣服,它能把绿光、蓝光都给吸收了,只有红光能从它内部弹出来,因此呈现红色
能量守恒¶
根据热力学第一定律( 能量守恒 定律),我们有以下推论
- 记入射光的总能量为1(单位1)
- 镜面反射系数\(k_s\):假设镜面反射部分占总能量的\(k_s\)(如\(k_s=0.8\),即代表80%的能量参与镜面反射作用)
- 漫反射系数\(k_d\):根据能量守恒定律,参与漫反射部分的能量即占总能量的\(k_d = 1-k_s\)(即\(k_d=0.2\))
[Tips] 对于漫反射系数,还需要乘以一个逆金属度
- 根据上面的例子,其中只是20%的能量 参与 漫反射作用的,但实际上不一定20%的能量都能从物体里面出来
- 这要根据物体的材质,如果是金属材质,它反射出来的能量几乎为0,因为光线进入物体之后,会被金属完全吸收
- 金属度
metallic
:用于表达此物体材料的金属度。取值范围是[0, 1],1完全是金属,0是介电质(非金属)
vec3 kD = vec3(1.0) - kS; //漫反射系数(能量守恒定律)
kD *= 1.0 - metallic; //乘以逆金属度
//若metallic=1.0,则kD=0,表示没有光线能出来
//若metallic=0.0,则表示参与漫反射作用的所有能量都能从物体内部弹出来
总结¶
根据上面的原理可知,一束光打到物体表面呈现出来的颜色,由两部分构成:漫反射(diffuse)、镜面反射(specular)。
\[
f_r = k_d * f_{diffuse} + k_s * f_{specular}
\]
关于这两项(\(f_{diffuse}\)、\(f_{specular}\))的求解,其实有很多种公式,因此衍生出了很多种BRDF,每一种都能近似得出 “物体表面对于光的反应” ,都很逼真。但是当前很多实时渲染管线使用的都是Cook-Torrance BRDF模型,本文不再赘述,请看后续文章。
伪代码如下:
vec3 evaluateBRDF(const PixelParams pixel, const Light light, const PbrMaterial material)
{
//计算一些参数,如NoV, NoL, NoH, LoH等,后续要用
//..
vec3 specular = evaluateSpecular(...); //镜面反射的颜色
vec3 diffuse = evaluateDiffuse(...); //漫反射的颜色
//两个系数
vec3 kS = material.Fresnel; //镜面反射系数 = 菲涅尔项(可以有别的算法)
vec3 kD = vec3(1.0) - kS; //漫反射系数(能量守恒定律)
kD *= 1.0 - metallic; //乘以逆金属度
return kD * diffuse + kS * specular;
}