跳转至

1 BRDF

在上一篇文章当中,我们谈到了BRDF公式,它是用来计算一个光源对物体表面的影响结果,在图形学中,这个结果我们通常用RGBA颜色来表示。这一节,将展开谈谈BRDF。

BRDF帮助我们计算出:一条光线 打到物体表面一点上,应该成什么颜色。

  • BRDF,Bidirectional Reflective Distribution Function,双向反射分布函数

镜面反射与漫反射

根据物理定律,我们知道当一束入射光(incident light)打在一个表面上时,将分成两部分:镜面反射和漫反射。

它们还有很多别名:

  • 镜面反射:specular reflection、高光反射
  • 漫反射:diffuse reflection、折射+散射

镜面反射

根据光的反射定律,当一束光碰撞到物体表面上时,它的一部分能量会沿着与平面法向量的对称方向反射出去,而不进入物体,这一部分即被称为“镜面反射”。

如下图Specular highlights箭头所指区域,即是“镜面反射”的效果

  • 白色:由于入射光是白光,而“镜面反射”是光线撞到表面后,直接反射出去的,因此“镜面反射”也是白光。
  • 亮亮的:镜面反射光的方向一般都集中在上图的\(B\)方向的周围,因此看起来亮亮的。这与漫反射不同,漫反射的光线是四面八方的,它的能量被均摊到四面八方了。

镜面反射与表面的粗糙度有关

  1. 如果表面越粗糙,下图中\(f_r\)区域就越大。那意味着,在物体表面上高光区域很大,但强度较弱
  2. 如果表面越光滑,下图中的\(f_r\)区域就越窄,直到反射光全集中在镜面方向(入射光轴对称的方向),这意味着,在物体表面上高光区域很小,但强度非常大,非常刺眼

漫反射

根据物理定律,我们有以下认识(如下图中的橘色光线)

  1. 当一束光碰撞到表面上时,它的一部分能量会折射进物体内部
  2. 物体由无数微小的粒子所组成,光线进入物体内部之后,会与这些粒子再次发生碰撞,在粒子间“弹来弹去”(即在物体内部发生 散射
  3. 在碰撞过程中,粒子会吸收光线的一部分能量,然后转为热量
    1. 若在传播过程中,光线的某条分支能量耗尽,光线就出不去了,全被吸收转换为了热量
    2. 若在传播过程中,光线能量未能被全部吸收,就成功离开表面,构成所谓的“漫反射光“,它们将协同构成物体表面的颜色(只是漫反射部分的颜色)

漫反射:经过折射、散射之后,还能从物体内部射出表面的光线。它们的方向随机,但各个方向的能量可以假定为相同。

  • 如下图,Diffuse reflection箭头所值即是漫反射作用下产生的颜色
  • 漫反射其实是物体的主要颜色,从上面交代的物理原理可知,它是物体的主要颜色,也直接取决于物体的材质。比如某个红色衣服,它能把绿光、蓝光都给吸收了,只有红光能从它内部弹出来,因此呈现红色

能量守恒

根据热力学第一定律( 能量守恒 定律),我们有以下推论

  1. 记入射光的总能量为1(单位1)
  2. 镜面反射系数\(k_s\):假设镜面反射部分占总能量的\(k_s\)(如\(k_s=0.8\),即代表80%的能量参与镜面反射作用)
  3. 漫反射系数\(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;
}