这篇推文是"Advanced Global Illumination 2nd"这本书第二章其中一部分的笔记,这本书非常好,但第二章有些地方没有讲细(可能是大佬的常识),作为内容的补充,这篇文章主要整理介绍Cook-Torrance BRDF包含的三个函数,并在最后,在ShaderToy里完成一个基于Cook-Torrance的光照模型。
双向反射分布函数
我们看到一个表面,实际上是周围环境的光照射到表面上,然后表面将一部分光反射到我们眼睛里。双向反射分布函数BRDF-Bidirectional Reflectance Distribution Function就是描述表面入射光和反射光关系的。
点处的BRDF定义为在出射方向上反射的相对辐射亮度与通过不同立体角入射的相对辐照度之比。BRDF表示为
其中是入射光方向,是观察方向,也就是反射光方向。
由此可以得出入射与反射亮度之间的关系。
接下来就是分析Cook-Torrance的着色模型。
BRDF性质
非负性
亥姆霍兹互反律
能量守恒
微平面理论
大多数真实世界的表面不是光学上光滑的,但是具有比光波长大得多但比像素小的尺度的不规则性。 这种微观几何(microgeometry)变化导致每个表面点反射(和折射)不同方向的光:材质的部分外观组成是这些反射和折射方向的聚合结果。
在微观尺度上,表面越粗糙,反射越模糊,因为表面取向与整个宏观表面取向的偏离更强。
Cook-Torrance 着色模型
Cook-Torrance着色模型就是基于微平面理论的一种着色模型。
Cook-Torrance BRDF的镜面反射部分包含三个函数,此外分母部分还有一个标准化因子 。字母D/F/G分别代表着一种类型的函数,各个函数分别用来近似的计算出表面反射特性的一个特定部分。三个函数分别为法线分布函数(Normal Distribution Function),菲涅尔方程(Fresnel Rquation)和几何函数(Geometry Function)。
法线分布函数:估算在受到表面粗糙度的影响下,取向方向与中间向量一致的微平面的数量。这是用来估算微平面的主要函数。
几何函数:描述了微平面自成阴影的属性。当一个平面相对比较粗糙的时候,平面表面上的微平面有可能挡住其他的微平面从而减少表面所反射的光线。
菲涅尔方程:菲涅尔方程描述的是在不同的表面角下表面所反射的光线所占的比率。
以上的每一种函数都是用来估算相应的物理参数的,而且你会发现用来实现相应物理机制的每种函数都有不止一种形式。它们有的非常真实,有的则性能高效。你可以按照自己的需求任意选择自己想要的函数的实现方法。
Fresnel因子
电磁波碰到物体表面时,会产生反射波和透射波。反射波的能量等于入射电磁波的能量减去透射电磁波的能量。值得一提的是,菲涅尔反射的方程可以通过麦克斯韦电磁学方程组推导出来,因为本质上讲菲涅尔反射其实是使用的波动理论来解释光的反射现象。
光又是一种电磁波,当光线碰撞到一个表面的时候,菲涅尔方程会根据观察角度告诉我们被反射的光线所占的百分比。利用这个反射比率和能量守恒原则,我们可以直接得出光线被折射的部分以及光线剩余的能量。
当垂直观察的时候,任何物体或者材质表面都有一个基础反射率,但是如果以一定的角度往平面上看的时候所有反光都会变得明显起来。比如这张图,河流近处的能看见河底,河流远处却是反射的天空与环境。这种现象因菲涅尔而闻名,并体现在了菲涅尔方程之中。
入射光的电场可以分解为相对某一平面的极化分量,通常选择包含表面法向量和指向光源的方向的平面为极化平面,与该平面平行的分量称为极化分量,垂直的分量称为极化分量。
对于偏振
利用,并取可得
对于偏振
利用,并取可得
入射光线与折射光线的方向由斯涅尔定律约束
对某一波长,Fresnel因子给出这两个极化分量的反射数值的表达式如下:
其中,为入射角,为与波长相关的透射角。图形学中通常考虑光是非偏振光。对于非偏振光,可用以上极化分量的平均值作为波长为的电磁波的Fresnel因子。
float fresnel(float cosi, float cost, float etai,
float etat) {
float rs = (etat * cosi - etai * cost)
/ (etat * cosi + etai * cost);
float rp = (etai * cosi - etat * cost)
/ (etai * cosi + etat * cost);
return (rs * rs + rp * rp) * 0.5f;
}
Fresnel-Schlick近似法
鉴于导体的菲涅耳方程较复杂,Schlick提供了一个近似的函数:
表示平面的基础反射率,它是利用所谓折射指数(Indices of Refraction)/IOR计算得出的。
Fresnel Schlick近似可以用代码表示为:
vec3 fresnelSchlick(float cosTheta, vec3 F0)
{
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
通用的折射率IOR与的关系式如下
其中,和分别为两种介质的折射率。通常假设近似于空气的折射率,并用替换,于是,上式可以简化为:
这就是我们通常看到的和折射率之间的转换公式。
在有些渲染器中,会直接采用折射率IOR来表示材质的属性,以下是一张渲染器中实数折射率、粗糙度二维材质的渲染对照图
[图片上传失败...(image-faedc3-1587550500025)]
材质属性
由于光由电磁波组成,因此,物质的光学特性与其导电特性密切相关。我们通常把导电性较差的材质,如煤、人工晶体、琥珀、陶瓷等称为绝缘体。而把导电性比较好的金属如金、银、铜、铁、锡、铝等称为导体。以及将导电性质介于导体和绝缘体之间的材质称为半导体。
即根据导电特性,可将现实生活中的物质分为三个主要光学类别:
- 电介质-Dielectrics,又称绝缘体-Insulators,非金属
- 半导体-Semiconductors
- 金属-Metals,又称导体-Conductors
附上浅墨大佬的PBR白皮书的常见材质参考速查图表
[图片上传失败...(image-ae097a-1587550500025)]
微平面分布函数
法线分布函数,即微平面分布函数,描述了平面法线分布的概率,即具有正确朝向的微表面法线浓度。微平面的法线分布函数描述了微观表面上的表面法线的统计分布。
可以将法线分布函数理解为微观几何表面区域上的微平面表面法线的统计分布。对在整个微平面法线上积分,会得到微表面的面积。
一般我们用宏观表面的半矢量来表示微观表面法线,因为仅的表面点的朝向才会将光线反射到视线的方向,其他朝向的表面点对BRDF没有贡献(正负相互抵消)。
性质
- 微平面法线密度始终为非负值:
- 微表面的总面积始终不小于宏观表面总面积:
- 任何方向上微观表面投影面积始终与宏观表面投影面积相同:
- 若观察方向为法线方向,则其积分可以归一化。即时,有:
Beckmann分布
Beckmann分布是光学业界开发的第一批微平面模型中使用的法线分布。也是Cook-Torrance BRDF在提出时选择的NDF。
给定中间向量,微平面分布函数返回法向量与中间向量相同的微平面的比例。对于粗糙平面,即各向同性表面的微平面分布函数(Beckmann分布函数)为
其中,为微平面斜率的均方根,表示了微平面法向量的分布情况。
的值越大,对应的平面越粗糙,微平面的法向量分布越广。的值越小,表面越光滑,微平面法向量分布范围越窄,可以产生锐利的高光效果。
UE4中对Beckmann分布的实现代码如下
float D_Beckmann( float a2, float NoH )
{
float NoH2 = NoH * NoH;
return exp( (NoH2 - 1) / (a2 * NoH2) ) /
( PI * a2 * NoH2 * NoH2 );
}
GGX分布
GGX,即Trowbridge-Reitz分布,最初由Trowbridge和Reitz推导出,在Blinn 1977年的论文中也有推荐此分布函数,但一直没有受到图形学界的太多关注。30多年后,Trowbridge-Reitz分布被Walter等人独立重新发现,并将其命名为GGX分布。
在重新发现并提出GGX分布之后,GGX分布采用风潮开始在电影和游戏行业中广泛传播,成为了如今游戏行业和电影行业中最常用的法线分布函数。
在流行的模型中,GGX拥有最长的尾部。这是GGX能够日益普及的主要原因。
使用GLSL代码编写的Trowbridge-Reitz GGX如下
float D_GGX_TR(vec3 N, vec3 H, float a)
{
float a2 = a*a;
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH*NdotH;
float nom = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return nom / denom;
}
各向异性
以上函数是各向同性的,也就是绕法向量旋转时函数值不变,当观察方向和指向光源的方向不变,并且它们与法向量之间的夹角不变时,微平面的分布也保持不变。
然而,很多表面在不同方向上的粗糙度不同,这些表面被称为各向异性的反射面,包括雾面金属、毛发和织物等材料表面都属于这类反射面。
有些表面有多重粗糙度,这种情况下,可用不同粗糙度微平面分布函数的加权平均来表示该表面的微平面分布函数,如下式所示。
Anisotropic Beckmann Distribution
对于各向异性的表面,Beckmann微平面分布函数为
其中,是一个二维粗糙度向量,和分别表示与切线方向平行和垂直的斜率均方根,向量是中间向量到切平面的规范化投影。
以下为UE4中Anisotropic Beckmann分布的Shader实现代码:
// Anisotropic Beckmann
float D_Beckmann_aniso( float ax, float ay, float NoH,
float3 H, float3 X, float3 Y )
{
float XoH = dot( X, H );
float YoH = dot( Y, H );
float d = - (XoH*XoH / (ax*ax) + YoH*YoH /(ay*ay))
/ NoH*NoH;
return exp(d) /
( PI * ax*ay * NoH * NoH * NoH * NoH );
}
Anisotropic GGX Distribution
各项异性的GGX分布形式如下
以下为UE4中Anisotropic Beckmann分布的Shader实现代码:
// Anisotropic Beckmann
float D_Beckmann_aniso( float ax, float ay, float NoH,
float3 H, float3 X, float3 Y )
{
float XoH = dot( X, H );
float YoH = dot( Y, H );
float d = - (XoH*XoH / (ax*ax) + YoH*YoH / (ay*ay))
/ NoH*NoH;
return exp(d) /
( PI * ax*ay * NoH * NoH * NoH * NoH );
}
几何衰减因子
在PBR中,几何衰减因子是一个0到1之间的标量,描述了微平面自阴影的属性。照向一个微平面的光线可能被相邻的微平面阻断,而照不到该微平面。微平面的反射光线也可能被相邻微平面阻断而反射不出来。
这会导致镜面反射的高光中出现轻微的暗纹,与表面的几何衰减系数相关。被阻断的光线基本沿任意方向散射出去,最后形成表面的漫反射。
几何衰减因子与法线分布函数作为Microfacet Specular BRDF中的重要两项,两者之间具有紧密的联系:
- 几何衰减因子的解析形式的确认依赖于法线分布函数。
- 法线分布函数需要结合几何衰减因子,得到有效的法线分布强度。
V-cavity
通过假设微平面总形成型槽结构,可以导出表面粗糙度阻断光线的大约数量。通过简单的三角学知识,可以推导出某一微平面的反射光线被相邻微平面阻断后,反射到观察者的光能表达式。设微平面的宽度为,其中观察者可见部分为,到达观察者的光线为,通过上图计算,简单推导如下。
经过上面计算,得出几何衰减因子的公式为
表示微平面的入射光和反射光中被相邻微平面阻断的部分。
Schlick-GGX
我们将要使用的几何函数是GGX与Schlick-Beckmann近似的结合体,这是UE4在SIGGRAPH 2013上公布的方案为基于Schlick近似。
这里的是基于几何函数是针对直接光照还是针对IBL光照的重映射(采用了Disney对粗糙度的重映射) 。从而使几何项的粗糙度变化更加平滑,更便于美术人员的使用。
为了有效的估算几何部分,需要将观察方向(几何遮蔽-Geometry Obstruction)和光线方向向量(几何阴影-Geometry Shadowing)都考虑进去。可以使用Smith’s method来把两者都纳入其中,即:
使用Smith’s method与Schlick-GGX作为可以得到如下所示不同粗糙度的视觉效果
使用GLSL编写的几何函数代码如下。
float GeometrySchlickGGX(float NdotV, float k)
{
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
float GeometrySmith(vec3 N, vec3 V, vec3 L, float k)
{
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float ggx1 = GeometrySchlickGGX(NdotV, k);
float ggx2 = GeometrySchlickGGX(NdotL, k);
return ggx1 * ggx2;
}
性质
标量性
几何衰减因子是0和1之间的一个标量
低粗糙度下几何函数大多情况下数值接近1
[图片上传失败...(image-5b0a26-1587550500025)]
对称性
几何函数在两个可见方向,即出射和入射上是对称的:
同向可见性
几何函数从宏观表面正面方向上无法看到微表面的背面,反之亦然:
拉伸不变性
微表面几何轮廓具有拉伸不变性。拉伸微表面轮廓就像拉伸一张图片,即在一个维度上乘以常数因子,不会更改微表面轮廓的拓扑结构:拉伸后,遮挡的光线仍会被遮挡,未遮挡的光线仍未被遮挡。
当微表面轮廓中涉及的所有斜率同时缩放时,遮蔽概率对于配置拉伸是不变的。这包括微表面的斜率和与出射方向相关的斜率。它们都是通过拉伸因子的倒数来缩放的。因此,斜率宽度的分布也被反向拉伸因子拉伸。如下图。
[图片上传失败...(image-16a359-1587550500025)]
Cook-Torrance 渲染方程
随着Cook-Torrance BRDF中所有元素都介绍完毕,我们现在可以将前文中的基于物理的BRDF着色模型纳入到最终的渲染方程里。
带入第二种弗雷德霍姆方程的渲染方程中。根据能量守恒,在某个点和特定初涉方向上的总出射辐射亮度是发射的辐射亮度和在该方向上该表面点处反射的辐射亮度的总和。
Cook-Torrance的BRDF方程的GLSL代码
vec3 cooktorrance(vec3 pos,vec3 cameraPos,vec3 lightPos,
vec3 lightIntensity,float roughness) {
vec3 N = GetNormal(pos);
vec3 V = normalize(cameraPos - pos);
vec3 F0 = vec3(0.04);
F0 = mix(F0, albedo, metallic);
// reflectance equation
vec3 Lo = vec3(0.0);
vec3 L = normalize(lightPos- pos);
vec3 H = normalize(V + L);
float distance2 = length(lightPos- pos);
float attenuation = 1.0 / (distance2 * distance2);
vec3 radiance = lightIntensity * attenuation;
// cook-torrance brdf
float NDF = DistributionGGX(N, H, roughness);
float G = GeometrySmith(N, V, L, roughness);
vec3 F = fresnelSchlick(max(dot(H, V),0.0),F0);
vec3 kS = F;
vec3 kD = vec3(1.0) - kS;
kD *= 1.0 - metallic;
vec3 nominator = NDF * G * F;
float denominator = 4.0*max(dot(N, V),0.0)*
max(dot(N, L), 0.0) + 0.001;
vec3 specular = nominator / denominator;
// add to outgoing radiance Lo
float NdotL = max(dot(N, L), 0.0);
Lo += (kD*albedo/PI + specular)*radiance*NdotL;
return Lo;
}
最后shadertoy里的呈现。
参考阅读
https://learnopengl.com/PBR/Theory
http://advances.realtimerendering.com/s2014/epic/TemporalAA.pptx
https://80.lv/articles/the-future-of-real-time-rendering-with-lumberyard/
https://github.com/EpicGames/UnrealEngine
https://github.com/QianMo/PBR-White-Paper/
http://www.cs.utah.edu/~michael/brdfs/jgtbrdf.pdf
http://www.disneyanimation.com/technology/brdf.html