Filament 中的物理渲染(翻译)

291 阅读8分钟

image.png

1 关于

本文档是Filament项目的一部分。要报告本文档中的错误,请使用项目的问题跟踪器

1.1 作者

2 概述

Filament 是一个面向 Android 的基于物理的渲染(PBR)引擎。Filament 的目标是为 Android 开发人员提供一套工具和 API,使他们能够轻松创建高质量的 2D 和 3D 渲染。 本文档的目标是解释 Filament 中使用的材质和光照模型背后的方程和理论。本文档旨在作为 Filament 贡献者或对引擎内部工作原理感兴趣的开发人员的参考。我们将根据需要提供代码片段,以尽可能清晰地展示理论与实践之间的关系。 本文档不是设计文档,它专注于算法,其内容可用于在任何引擎中实现 PBR。但是,本文档解释了我们为什么选择特定的算法/模型而不是其他算法/模型。 除非另有说明,本文档中的所有 3D 渲染均在引擎中生成(原型或生产)。这些 3D 渲染中的许多是在 Filament 开发的早期阶段捕获的,并不反映最终质量。

2.1 原则

实时渲染是一个活跃的研究领域,对于每个需要实现的功能,都有大量的方程式、算法和实现可供选择(例如,书籍《实时阴影渲染》就总结了数十种阴影渲染技术,共计400页)。因此,我们必须首先确定我们的目标(或原则,参考布伦特·伯利在迪士尼发表的开创性论文《基于迪士尼物理的着色》Burley12),然后才能做出明智的决策。

实时移动性能

我们的主要目标是设计和实现一个能够在移动平台上高效运行的渲染系统。主要目标将是OpenGL ES 3.x级别的GPU。

质量

我们的渲染系统将强调整体图像质量。不过,我们会接受质量妥协以支持中低性能 GPU。

使用方便

艺术家需要能够经常、快速地迭代他们的资产,我们的渲染系统必须允许他们直观地做到这一点。因此,我们必须提供易于理解的参数(例如,无镜面反射强度)。

我们也明白,并非所有开发人员都能与艺术家合作。我们系统基于物理的方法将允许开发人员制作视觉上合理的材料,而无需了解我们实现背后的理论。

对于艺术家和开发人员来说,我们的系统将依靠尽可能少的参数来减少试错,并允许用户快速掌握材质模型。

此外,参数值的任何组合都应导致物理上合理的结果。物理上难以置信的材料很难创建。

熟悉

我们的系统应该在任何地方尽可能使用物理单位:以米或厘米为单位的距离、以开尔文为单位的色温、以流明或坎德拉为单位的光单位等。

灵活性

基于物理的方法不得排除非现实渲染。例如,用户界面将需要未照明的材料。

部署大小

虽然与本文档的内容没有直接关系,但值得强调的是,我们希望使渲染库尽可能小,以便任何应用程序都可以捆绑它,而不会将二进制文件增加到不需要的大小。

2.2 基于物理的渲染

我们选择采用PBR是因为从艺术和制作效率的角度来看,它具有许多优势,并且与我们的目标相容。 物理渲染是一种渲染方法,与传统的实时模型相比,它提供了更准确的材质表示以及它们与光线的相互作用。PBR方法核心的材质和光照分离使得更容易创建在所有光照条件下看起来准确的逼真资产。

3 符号表示

本文中找到的方程式使用表1中描述的符号。

符号描述
vv视图单位向量
ll 入射光单位矢量
nn 表面法线单位向量
hhl和v之间的半单位向量
ffBRDF(双向反射分布函数)
fdf_dBRDF 的漫反射分量
frf_rBRDF 的镜面反射分量
αα粗糙度,从使用输入perceptualRoughness重新映射。
σσ漫反射率
ΩΩ球形域
f0f_0法线入射时的反射率
f90f_{90}入射角处的反射率
χ+(a)χ^+(a)Heaviside 函数(如果 a>0 则为1,否则为0)
niorn_{ior}界面的折射率(IOR)
nl⟨n⋅l⟩点积限制在[0..1]
a⟨a⟩饱和值(限制在[0..1]范围内)

表1:  符号定义

4 着色器

下面的部分描述了多种材料模型,以简化对各种表面特征(如各向异性或透明涂层)的描述。然而,在实践中,其中一些模型被合并为一个单一的模型。例如,标准模型、透明涂层模型和各向异性模型可以合并成一个单一、更灵活和强大的模型。请参考材料文档,以获取在Filament中实现的材料模型的描述。

4.1 标准着色器

我们模型的目标是代表标准材质的外观。材料模型在数学上由BSDF(双向散射分布函数)描述,它本身由两个其他函数组成:BRDF(双向反射分布函数)和BTDF(双向透射函数)。

由于我们的目标是对常见表面进行建模,我们的标准材料模型将侧重于BRDF,并忽略BTDF,或者对其进行极大的近似。因此,我们的标准模型只能正确地模拟具有短平均自由程的反射性、各向同性、介质或导电表面。

BRDF将标准材料的表面响应描述为由两个项组成的函数:

  • 一个漫反射部分, 或者 fdf_d
  • 一个镜面反射部分, 或者 frf_r

表面、表面法线、入射光之间的关系如图1所示(我们暂时忽略表面下散射)。

图1:使用BRDF模型与表面相互作用,其中包括漫反射项$f_d$和镜面反射项$f_r$。

完整的表面响应可以表示为:(1)

f(v,l)=fd(v,l)+fr(v,l)f(v,l)=f_d(v,l)+f_r(v,l)

这个方程描述了来自单一方向入射光对表面响应的特征。完整的渲染方程需要对整个半球体上的光线进行积分。

我们通常遇到的表面并不是由一个平坦的表面构成的,因此我们需要一个能够描述光与不规则界面相互作用的模型。

微平面BRDF是一个用于该目的的好的物理上可行的BRDF。这样的BRDF认为表面在微观层面上并不平滑,而是由大量随机排列的平面碎片组成,称为微平面。图2显示了在微观层面上平坦界面和不规则界面之间的区别:

图2:由微平面模型模拟的不规则表面(左)和平坦界面(右)

只有微面的法线朝向介于光线方向和视线方向之间的一半位置时,才会反射可见光,如图3所示。

image.png

图3:微表面

但是,并不是所有具有正确定向法线的微面都会贡献反射光,因为双向反射分布函数(BRDF)考虑了遮蔽和阴影效应。这一点在图4中有所示意。

image.png

图4:微面的遮蔽和阴影效应。

微面BRDF受到一个描述表面在微观层面上是光滑(低粗糙度)还是粗糙(高粗糙度)的粗糙度参数的极大影响。表面越光滑,越多的微面会排列一致,反射光也就越显著。表面越粗糙,越少的微面朝向相机,反射后的入射光会远离相机散射,使得镜面高光显得模糊。

图5展示了不同粗糙度的表面以及光线与它们的相互作用。

image.png

图5:变化的粗糙度(从左到右,粗糙到光滑)以及产生的BRDF镜面成分波纹

> #### 关于粗糙度
>在本文档中的着色器代码片段里,用户设置的粗糙度参数被称为 `perceptualRoughness`。 >而变量 `roughness` 是经过第4.8节解释的重新映射后的 `perceptualRoughness`。

微面模型由以下方程描述(其中x代表镜面或漫反射分量):(2)

fr(v,l)=1nvnlΩD(m,α)G(v,l,m)fm(v,l,m)(vm)(lm)dmf_r(v, l)= \frac{1}{|n \cdot v||n \cdot l|} \int_{\Omega}D(m, \alpha)G(v, l, m)f_m(v, l, m)(v \cdot m)(l \cdot m)dm

术语DD表示微平面的分布(这个术语也被称为NDF或正态分布函数)。正如图5所示,这个术语在表面的外观中起着至关重要的作用。

术语GG模拟微表面的可见性(或遮挡或阴影遮罩)。

由于这个方程对于镜面和漫反射组件都是有效的,区别在于微平面BRDF fmf_m

image.png

图6:对单个点处的表面响应进行建模需要在微观层面进行积分。

上图显示,在宏观层面上,表面被认为是平坦的。这有助于通过假设从单一方向照亮的阴影片段对应于表面的单一点来简化我们的方程。

然而,在微观层面上,表面并不平坦,我们不能再假设单一光线(不过我们可以假设入射光线是平行的)。由于微面会根据一束平行入射光线将光散射到不同方向,我们必须对半球面上的表面响应进行积分,如上图中所示的ΩmΩ_m

显然,对每个阴影片段的微面半球进行完整积分计算是不切实际的。因此,我们将依赖于镜面和漫反射分量积分的近似值。

4.2 导体和介质

为了更好地理解下面显示的一些方程式和行为,我们必须首先清楚地了解金属(导体)和非金属(介质)表面之间的区别。 我们之前看到,当入射光线照射到由BRDF控制的表面时,光会以两个独立的部分反射:漫反射和镜面反射。这种行为的模拟如图7所示是直接的。

image.png

图7:BSDF 的 BRDF 部分的建模

这种模拟是光线实际与表面相互作用的简化。实际上,部分入射光会穿透表面,在内部散射,然后再次以漫反射形式退出表面。这种现象在图8中有所说明。

image.png

图8:漫射光的散射

导体和介质之间的区别在于这里。纯金属材料没有次表面散射,这意味着没有漫反射成分(我们将在后面看到这对镜面反射成分的感知颜色产生影响)。介质发生散射,这意味着它们具有镜面和漫反射成分。

因此,在适当建模 BRDF 时,我们必须区分介质和导体(为了清晰起见,不显示散射),如图 9 所示。

image.png

图9:介质和导体表面的BRDF建模

4.3 能量守恒

能量守恒是基于物理的渲染中良好BRDF的关键组成部分之一。 能量守恒的BRDF声明,镜面和漫反射的总能量小于入射能量的总量。 如果没有能量守恒的BRDF,艺术家必须手动确保表面反射的光线永远不会比入射光更强。

4.4 镜面反射双向散射函数

就镜面项而言,frf_r是可以用菲涅尔定律建模的镜面BRDF,在微平面模型积分的Cook-Torrance近似中采用符号F。 (3)

fr(v,l)=D(h,α)G(v,l,α)F(v,h,f0)4(nv)(nl)f_r(v,l)=\frac{D(h,α)G(v,l,α)F(v,h,f0)}{4(n⋅v)(n⋅l)}

考虑到我们的实时约束,我们必须对三个术语 D、G 和 F 使用近似值。Karis13a 汇编了一份关于这三个术语的公式的出色清单,可以与 Cook-Torrance 镜面 BRDF 一起使用。接下来的部分描述了我们选择的这些术语的方程式。

4.4.1 法线分布函数(镜面D)

Burley12观察到长尾正态分布函数(NDF)很适合现实世界的表面。在Walter07中描述的GGX分布是一个具有长尾衰减和高光短峰的分布,具有适合实时实现的简单公式。它也是现代基于物理的渲染器中受欢迎的模型,相当于Trowbridge-Reitz分布。 (4)

DGGX(h,α)=α2π((nh)2(α21)+1)2D_{GGX}(h,α)=\frac{α^2}{π((n⋅h)^2(α^2−1)+1)^2}

NDF的GLSL实现, 如图 1 中所示,简单高效。

float D_GGX(float NoH, float roughness) {
    float a = NoH * roughness;
    float k = roughness / (1.0 - NoH * NoH + a * a);
    return k * k * (1.0 / PI);
}

清单 1:在 GLSL 中实现镜面 D 项

我们可以通过使用半精度浮点数来改进这个实现。这种优化需要对原方程进行更改,因为在使用半浮点数计算 1(nh)21−(n⋅h)^2 时存在两个问题。首先,在 (nh)2(n ⋅h)^2 接近 1(高亮)时,该计算受到浮点数消除的影响。其次, nhn⋅h 在 1 左右没有足够的精度。 解决方案涉及拉格朗日恒等式:(5)

a×b2=a2b2(ab)2|a×b|^2=|a|^2|b|^2−(a⋅b)^2

由于 nnhh 均为单位向量,n×h2=1(nh)2| n × h |^2 = 1 - ( n ⋅ h )^2。这使得我们能够通过使用简单的叉乘来直接计算 1(nh)21 - ( n ⋅ h )^2。清单 2 展示了最终优化的实现。

#define MEDIUMP_FLT_MAX    65504.0
#define saturateMediump(x) min(x, MEDIUMP_FLT_MAX)

float D_GGX(float roughness, float NoH, const vec3 n, const vec3 h) {
    vec3 NxH = cross(n, h);
    float a = NoH * roughness;
    float k = roughness / (dot(NxH, NxH) + a * a);
    float d = k * k * (1.0 / PI);
    return saturateMediump(d);
}

清单2:针对fp16优化的GLSL中的高光D项的实现

4.4.2 几何遮蔽(镜面 G)

Eric Heitz 在 Heitz14 中表明,Smith 几何阴影函数是正确和准确的 GG 项。Smith 的表示如下:(6)

G(v,l,α)=G1(l,α)G1(v,α)G(v,l,α)=G1(l,α)G1(v,α)

G可以遵循几种模型,并且通常设定为GGX公式:(7)

G1(v,α)=GGGX(v,α)=2(nv)nv+α2+(1α2)(nv)2G_1(v,α)=G_{GGX}(v,α)=\frac{2(n⋅v)}{n⋅v+\sqrt{α^2+(1−α^2)(n⋅v)^2}}

完整的Smith-GGX公式因此变为:(8)

G(v,l,α)=2(nl)nl+α2+(1α2)(nl)22(nv)nv+α2+(1α2)(nv)2G(v,l,α)=\frac{2(n⋅l)}{n⋅l+\sqrt{α^2+(1−α^2)(n⋅l)^2}}\frac{2(n⋅v)}{n⋅v+\sqrt{α^2+(1−α^2)(n⋅v)^2}}

我们可以观察到分红2(nl)2(n・l)2(nv)2(n・v)使我们能够通过引入能见度函数VV简化原始函数frf_r:(9)

fr(v,l)=D(h,α)V(v,l,α)F(v,h,f0)f_r(v,l)=D(h,α)V(v,l,α)F(v,h,f0)

其中:(10)

V(v,l,α)=G(v,l,α)4(nv)(nl)=V1(l,α)V1(v,α)V(v,l,α)=\frac{G(v,l,α)}{4(n⋅v)(n⋅l)}=V_1(l,α)V_1(v,α)

并且:(11)

V1(v,α)=1nv+α2+(1α2)(nv)2V_1(v,α)=\frac{1}{n⋅v+α^2+(1−α^2)(n⋅v)^2}

然而,Heitz指出,考虑微表面的高度以使掩模和阴影相关导致更准确的结果。他这样定义高度相关的Smith函数:(12)

G(v,l,h,α)=χ+(vh)χ+(lh)1+Λ(v)+Λ(l)G(v,l,h,α)=\frac{χ^+(v⋅h)χ^+(l⋅h)}{1+Λ(v)+Λ(l)}

(13)

Λ(m)=1+1+α2tan2(θm)2=1+1+α2(1cos2(θm))cos2(θm)2Λ(m)=\frac{−1+\sqrt{1+α^2tan^2(θm)}}{2}=\frac{−1+\sqrt{1+α^2\frac{(1−cos^2(θm))}{cos^2(θm)}}}{2}

nvn⋅v替换 cos(θm)cos(θm),我们得到:(14)

Λ(v)=12(α2+(1α2)(nv)2nv1)Λ(v)=\frac{1}{2}(\frac{\sqrt{α^2+(1−α^2)(n⋅v)^2}}{n⋅v}−1)

我们可以推导出能见度函数:(15)

V(v,l,α)=0.5nl(nv)2(1α2)+α2+nv(nl)2(1α2)+α2V(v,l,α)=\frac{0.5}{n⋅l\sqrt{(n⋅v)^2(1−α^2)+α^2}+n⋅v\sqrt{(n⋅l)^2(1−α^2)+α^2}}

在清单3中显示的能见度术语的GLSL实现比我们想要的要昂贵一些,因为它需要两次sqrt操作。

float V_SmithGGXCorrelated(float NoV, float NoL, float roughness) {
    float a2 = roughness * roughness;
    float GGXV = NoL * sqrt(NoV * NoV * (1.0 - a2) + a2);
    float GGXL = NoV * sqrt(NoL * NoL * (1.0 - a2) + a2);
    return 0.5 / (GGXV + GGXL);
}

清单3:GLSL中对高光V项的实现

在注意到所有平方根下的项都是平方,并且所有项都在 [ 0..1 ] 范围内后,我们可以通过近似来优化这个可见性函数:(16)

V(v,l,α)=0.5nl(nv(1α)+α)+nv(nl(1α)+α)V(v,l,α)=\frac{0.5}{n⋅l(n⋅v(1−α)+α)+n⋅v(n⋅l(1−α)+α)}

清单4:在GLSL中近似光滑V项的实现

Hammon17 提出了基于相同观察结果的相同近似,即平方根可以被移除。它通过将表达式重写为lerps来实现:(17)

V(v,l,α)=0.5lerp(2(nl)(nv),nl+nv,α)V(v,l,α)=\frac{0.5}{lerp(2(n⋅l)(n⋅v),n⋅l+n⋅v,α)}

4.4.3 菲涅尔(折射 F)

菲涅尔效应在物理上基于的材料的外观中起着重要作用。这个效应模拟了观察者所看到的从表面反射的光的数量取决于观察角度的事实。大片水域是体验这种现象的完美方式,就像图10所示。当直接俯视水面(正入射时),你可以看穿水。然而,当进一步朝远处看去(在接近光线的角度,在这里感知到的光线变得平行于表面时),你会看到水面上的镜面反射变得更加明显。

反射的光量不仅取决于观察角度,还取决于材料的折射率(IOR)。在正入射角(垂直于表面,或0°角)时,反射回来的光量记为f0f0,并且可以从折射率推导出来,我们将在4.8.3.2节中看到。在接近横向角时反射回来的光量记为f90f90,并且对于光滑材料接近100%。

image.png

图 10:菲涅尔效应在大面积水体上尤为明显

更正式地说,菲涅耳项定义了光在两种不同介质之间的界面上反射和折射的方式,或者反射和透射能量的比例。Schlick94描述了对于Cook-Torrance镜面BRDF的菲涅耳项的廉价近似:(18)

FSchlick(v,h,f0,f90)=f0+(f90f0)(1vh)5F_{Schlick}(v,h,f_0,f_{90})=f_0+(f_{90}−f_0)(1−v⋅h)5

常数f0f0表示在垂直入射时的镜面反射率,对于电介质是无色的,对于金属是有色的。实际值取决于界面的折射率。此术语的GLSL实现需要使用pow函数,如清单5所示,可以用几次乘法代替。

vec3 F_Schlick(float u, vec3 f0, float f90) {
    return f0 + (vec3(f90) - f0) * pow(1.0 - u, 5.0);
}

清单5:GLSL 中镜面反射项(specular F)的实现

这个 Fresnel 函数可以被视为在入射镜面反射率和在近乎垂直角度处的反射率之间进行插值,这里用 f90f90 表示。 对真实世界材料的观察表明,无论是介电体还是导体在近乎垂直角度处都表现出无色光的镜面反射,而在 90° 处 Fresnel 反射率为 1.0。更正确的 f90f90 在第 5.6.2 节中进行了讨论。

当将 f 设置为 1 时,对 Fresnel 项使用 Schlick 近似可以通过稍微重构代码来优化标量运算。结果如清单 6 所示。

vec3 F_Schlick(float u, vec3 f0) {
    float f = pow(1.0 - u, 5.0);
    return f + f0 * (1.0 - f);
}

清单6:在GLSL中对镜面F项进行标量优化

4.5 漫反射双向反射分布函数

在漫反射项中,fmf_m是朗伯函数,BRDF的漫反射项变为:(19)

fd(v,l)=σπ1nvnlΩD(m,α)G(v,l,m)(vm)(lm)dmf_d(v,l)=\frac{σ}{π}\frac{1}{|n⋅v||n⋅l|}∫_ΩD(m,α)G(v,l,m)(v⋅m)(l⋅m)dm

我们的实现将使用一个简单的朗伯BRDF,它假定微表面半球上的漫反射是均匀的:(20)

fd(v,l)=σπf_d(v,l)=\frac{σ}{π}

在实践中,漫反射σ会在后面乘以,如清单 8 所示。

float Fd_Lambert() {
    return 1.0 / PI;
}

vec3 Fd = diffuseColor * Fd_Lambert();

清单7:在GLSL中实现漫反射兰伯特BRDF

Lambertian BRDF 显然非常高效,并且提供了接近更复杂模型的结果。

然而,漫反射部分理想上应与镜面项一致,并考虑表面粗糙度。Disney漫反射BRDFBurley12和Oren-Nayar模型Oren94都考虑了粗糙度,并在入射角处产生一些反射。鉴于我们的限制,我们决定额外的运行时成本无法证明轻微增加质量的合理性。这种复杂的漫反射模型还使基于图像和球面谐波更难表达和实现。

为完整起见,Burley12中表达的迪士尼漫反射BRDF如下所示:(21)

fd(v,l)=σπFSchlick(n,l,1,f90)FSchlick(n,v,1,f90)f_d(v,l)=\frac{σ}{π}F_{Schlick}(n,l,1,f_{90})F_{Schlick}(n,v,1,f_{90})

其中:(22)

f90=0.5+2αcos2(θd)f_{90}=0.5+2⋅αcos2(θ_d)
float F_Schlick(float u, float f0, float f90) {
    return f0 + (f90 - f0) * pow(1.0 - u, 5.0);
}

float Fd_Burley(float NoV, float NoL, float LoH, float roughness) {
    float f90 = 0.5 + 2.0 * roughness * LoH * LoH;
    float lightScatter = F_Schlick(NoL, 1.0, f90);
    float viewScatter = F_Schlick(NoV, 1.0, f90);
    return lightScatter * viewScatter * (1.0 / PI);
}

清单 8:在GLSL中实现漫射Disney BRDF

图 11 显示了一个简单的 Lambertain 漫反射 BRDF 与高质量 Disney 漫反射 BRDF 之间的比较,使用完全粗糙的介电材质。为了进行比较,右侧的球体是镜像的。两种 BRDF 的表面响应非常相似,但 Disney 版本在接近的角度展示了一些漂亮的反射(仔细看球体左边缘)。

image.png

图 11:Lambertian 漫反射 BRDF(左)与 Disney 漫反射 BRDF(右)之间的比较

我们可以允许艺术家/开发人员根据他们所期望的质量和目标设备的性能选择迪士尼漫反射BRDF。然而需要注意的是,迪士尼漫反射BRDF并不遵守能量守恒定律。

4.6 标准模型摘要

镜面项: 采用 Cook-Torrance 镜面微平面模型,具有 GGX 法线分布函数,Smith-GGX 高度相关的能见度函数和 Schlick Fresnel 函数。

漫反射项: Lambertian 漫反射模型。

标准模型的完整 GLSL 实现显示在清单 9 中。

float D_GGX(float NoH, float a) {
    float a2 = a * a;
    float f = (NoH * a2 - NoH) * NoH + 1.0;
    return a2 / (PI * f * f);
}

vec3 F_Schlick(float u, vec3 f0) {
    return f0 + (vec3(1.0) - f0) * pow(1.0 - u, 5.0);
}

float V_SmithGGXCorrelated(float NoV, float NoL, float a) {
    float a2 = a * a;
    float GGXL = NoV * sqrt((-NoL * a2 + NoL) * NoL + a2);
    float GGXV = NoL * sqrt((-NoV * a2 + NoV) * NoV + a2);
    return 0.5 / (GGXV + GGXL);
}

float Fd_Lambert() {
    return 1.0 / PI;
}

void BRDF(...) {
    vec3 h = normalize(v + l);

    float NoV = abs(dot(n, v)) + 1e-5;
    float NoL = clamp(dot(n, l), 0.0, 1.0);
    float NoH = clamp(dot(n, h), 0.0, 1.0);
    float LoH = clamp(dot(l, h), 0.0, 1.0);

    // perceptually linear roughness to roughness (see parameterization)
    float roughness = perceptualRoughness * perceptualRoughness;

    float D = D_GGX(NoH, roughness);
    vec3  F = F_Schlick(LoH, f0);
    float V = V_SmithGGXCorrelated(NoV, NoL, roughness);

    // specular BRDF
    vec3 Fr = (D * V) * F;

    // diffuse BRDF
    vec3 Fd = diffuseColor * Fd_Lambert();

    // apply lighting...
    
    vec3 color = (Fd + Fr) * lightColor * NoL;
}

清单 9:在GLSL中编写BRDF

4.7 改善BRDF

我们在第 4.3 节中提到,能量守恒是良好 BRDF 的关键组成部分之一。不幸的是,先前探讨的 BRDF 存在两个问题,我们将在下面进行检查。

4.7.1 漫反射能量增益

Lambert漫反射BRDF并不考虑在表面反射的光线,因此不能参与漫反射事件。

[待办:谈论关于fr+fd的问题]

4.7.2 在镜面反射中的能量损失

我们之前介绍的Cook-Torrance BRDF试图在微平面层次上模拟多个事件,但通过考虑单次光线反弹来实现。这种近似可能会在高粗糙度时导致能量损失,表面并非能量保持不变。图12显示了为什么会发生这种能量损失。在单次反弹(或单次散射)模型中,光线击中表面后可以反射回另一个微平面,因此可能会因为遮蔽和阴影项而被丢弃。然而,如果我们考虑多次反弹(多次散射),同一条光线可能会逃离微平面区域并反射回观察者。

image.png

图 12:单次散射(左)与多次散射

根据这个简单的解释,我们可以直观地推断,表面越粗糙,能量丢失的几率就越高,因为没有考虑到多次散射事件。这种能量的损失似乎会使粗糙的材料变暗。金属表面特别容易受到影响,因为它们的反射都是镜面反射。这种变暗效应在图13中有所说明。通过多次散射,可以实现能量的保留,如图14所示。

image.png

图13:由于单次散射,粗糙度增加使暗化增加

image.png

图14:多次散射下的能量保持

我们可以使用一个白色炉子,一个设置为纯白色的统一照明环境,来验证BRDF的能量保持属性。当能量保持达到时,一个纯反射金属表面f0=1(f0=1)应该与背景无法区分,而不管表面的粗糙度。图15显示了在前几节中介绍的镜面BRDF下这样一个表面的外观。随着粗糙度的增加,能量的损失是显而易见的。相比之下,图16表明考虑多次散射事件可以解决能量损失问题。

image.png

图15:由于单次散射,粗糙度增加会使暗化程度增加

image.png

图16:多次散射的能量保留

在 [Heitz16] 中深入讨论了多次散射微平面BRDF。不幸的是,这篇论文仅提出多次散射BRDF的随机评估。因此,这种解决方案不适用于实时渲染。 Kulla 和 Conty 在 [Kulla17] 中提出了一种不同的方法。 他们的想法是在方程式23中显示的附加BRDF波瓣中添加一个能量补偿项。(23)

fms(l,v)=(1E(l))(1E(v))Favg2Eavgπ(1Eavg)(1Favg(1Eavg))f_{ms}(l,v)=\frac{(1−E(l))(1−E(v))F^2_{avg}E_{avg}}{π(1−E_{avg})(1−F_{avg}(1−E_{avg}))}

其中 E 为具有定向反照率的镜面 BRDF frf_r,其中 f0f_0 设为 1:(24)

E(l)=Ωf(l,v)(nv)dvE(l)=∫_Ωf(l,v)(n⋅v)dv

术语 EavgE_{avg}EE 的余弦加权平均值:(25)

Eavg=201E(μ)μdμE_{avg}=2∫^1_0E(μ)μdμ

类似地,FavgF_{avg}是菲涅尔项的余弦加权平均值:(26)

Favg=201F(μ)μdμF_{avg}=2∫^1_0F(μ)μdμ

词语 EEEavgE_{avg} 可以预先计算并存储在查找表中。当使用 Schlick 近似时,FavgF_{avg}可以大大简化:(27)

Favg=1+20f021F_{avg}=\frac{1+20f_0}{21}

这个新的波瓣与之前提到的原始单次散射波瓣结合在一起frf_r:(28)

fr(l,v)=fss(l,v)+fms(l,v)f_r(l,v)=f_{ss}(l,v)+f_{ms}(l,v)

Lagarde18 中,由于 Emmanuel Turquin 的贡献,Lagarde 和 Golubev 观察到,方程式 27 可以简化为 f0f_0 。他们还建议通过添加一个经过缩放的 GGX 镜面反射来应用能量补偿:(29)

fms(l,v)=f01E(l)E(l)fss(l,v)f_{ms}(l,v)=f_0\frac{1−E(l)}{E(l)}f_{ss}(l,v)

关键见解是 E(l)E(l) 不仅可以预先计算,而且可以与基于图像的照明预集成共享。因此,多次散射能量补偿公式为:(30)

fr(l,v)=fss(l,v)+f0(1r1)fss(l,v)f_{r}(l,v)=f_{ss}(l,v)+f_0(\frac{1}{r}−1)f_{ss}(l,v)

其中 rr 定义为:(31)

r=ΩD(l,v)V(l,v)nldlr=∫_ΩD(l,v)V(l,v)⟨n⋅l⟩dl

如果我们在第5.3节中呈现的DFG查找表中存储r,则可以以极低的成本实现镜面能量补偿。 清单10显示,实施是方程式30的直接转换。

vec3 energyCompensation = 1.0 + f0 * (1.0 / dfg.y - 1.0);
// Scale the specular lobe to account for multiscattering
Fr *= pixel.energyCompensation;

清单 10:能量补偿镜面反射波瓣

请参考第5.3节和第5.3.4.7节,了解如何推导和计算DFG查找表。

4.8 参数化

Disney的材质模型在[Burley12] 中描述得很好,但其众多参数使其在实时实现中变得不切实际。此外,我们希望我们的标准材质模型易于理解,也易于艺术家和开发人员使用。

4.8.1 标准参数

表2描述了满足我们约束条件的参数列表。

参数定义
BaseColor非金属表面的漫反射反照率,金属表面的镜面颜色
Metallic表面是否显示为电介质(0.0)或导体(1.0)。通常用作二进制值(0或1)
Roughness表面的感知光滑度(0.0)或粗糙度(1.0)。光滑表面呈现出明显的反射
Reflectance用于介电表面法线入射的菲涅尔反射率。这取代了明确的折射率
Emissive附加的漫反射率用于模拟发光表面(如霓虹灯等)。这个参数在具有HDR流程和泛光通道的管道中非常有用
Ambient occlusion定义了表面点能够接收到多少环境光。这是一个介于 0.0 和 1.0 之间的每像素阴影因子。这个参数将在照明(lighting)部分进行更详细讨论

表2:标准模型的参数

图17显示了金属、粗糙度和反射参数如何影响表面的外观。

image.png

图 17:从上到下依次是:变化的金属、变化的介电常数粗糙度、变化的金属粗糙度、变化的反射率

4.8.2 类型和范围

重要的是要了解我们材料模型中不同参数的类型和范围,描述在表3中。

参数类型和范围
BaseColor线性 RGB [0..1]
Metallic标量 [0..1]
Roughness标量 [0..1]
Reflectance标量 [0..1]
Emissive线性 RGB [0..1] + 曝光补偿
Ambient occlusion标量 [0..1]

表3:标准模型参数的范围和类型

请注意,此处描述的类型和范围是着色器预期的。当艺术家更直观时,API 和/或工具 UI 可以并且应该允许使用其他类型和范围指定参数。

例如,基色可以在sRGB空间中表示,并在发送到着色器之前转换为线性空间。 艺术家们也可以将金属度、粗糙度和反射参数表示为介于0到255之间的灰度值(从黑到白),这也很有用。

另一个例子: 可以将发射参数表示为色温和强度,以模拟黑体发射的光。

4.8.3 重映射

为了使标准材质模型对艺术家更易于使用且更直观,我们必须重新映射参数 baseColor、roughness 和 reflectance。

4.8.3.1 基础色重映射

材料的基本颜色受到该材料的“金属性”的影响。 绝缘体具有无色的镜面反射,但保留基色作为漫反射色。 另一方面,导体使用基色作为镜面颜色,并且没有漫反射组件。

因此,光照方程必须使用漫反射颜色和f0,而不是基本颜色。漫反射颜色可以很容易地从基本颜色计算出来,如图11所示。

vec3 diffuseColor = (1.0 - metallic) * baseColor.rgb;

清单 11:在GLSL中将基色转换为漫反射

4.8.3.2 反射重映射
介质

菲涅尔项依赖于f0f_0,即法线入射角处的高光反射率,并且在介电体中是色散的。我们将使用 Lagarde14 中描述的介电表面的重新映射:

f0=0.16reflectance2f_0=0.16⋅reflectance^2

f0f_0 映射到能够表示常见介质表面(4% 反射率)和宝石(8% 到 16%)的菲涅尔值范围是目标。 选择映射函数以便在输入反射率为 0.5(线性 RGB 灰度上为 128)时产生 4% 菲涅尔反射值。 图18 展示了这些常见数值以及它们与映射函数的关系。

image.png

图18:常见反射值

如果知道折射率(例如,空气-水界面的折射率为1.33),则菲涅尔反射率可以计算如下:

f0(nior)=(nior1)2(nior+1)2f_0(n_{ior})=(n_{ior}−1)^2(n_{ior}+1)^2

如果知道反射值,我们可以计算相应的折射率。

nior=21f01n_{ior}=\frac{2}{1−\sqrt{f_0}}−1

表格 4 描述了各种类型材料的可接受菲涅尔反射值(现实世界材料的值均不低于 2%)。

材质反射率折射率线性值
Water2%1.330.35
Fabric4% to 5.6%1.5 to 1.620.5 to 0.59
Common liquids2% to 4%1.33 to 1.50.35 to 0.5
Common gemstones5% to 16%1.58 to 2.330.56 to 1.0
Plastics, glass4% to 5%1.5 to 1.580.5 to 0.56
Other dielectric materials2% to 5%1.33 to 1.580.35 to 0.56
Eyes2.5%1.380.39
Skin2.8%1.40.42
Hair4.6%1.550.54
Teeth5.8%1.630.6
Default value4%1.50.5

表格 4:常见材料的反射率(来源:实时渲染第四版)

表5列出了一些金属的f0f_0值。这些数值以sRGB表示,并必须作为我们材质模型中的基础颜色使用。请参考附录,第9.1节,以了解这些sRGB颜色是如何从测定数据中计算得出的。

金属𝑓0𝑓_0 sRGB 值十六进制
Silver0.97, 0.96, 0.91#f7f4e8
Aluminum0.91, 0.92, 0.92#e8eaea
Titanium0.76, 0.73, 0.69#c1baaf
Iron0.77, 0.78, 0.78#c4c6c6
Platinum0.83, 0.81, 0.78#d3cec6
Gold1.00, 0.85, 0.57#ffd891
Brass0.98, 0.90, 0.59#f9e596
Copper0.97, 0.74, 0.62#f7bc9e

表 5:   常见金属的𝑓0𝑓_0

所有材料在临边角度时具有100%的菲涅尔反射率,因此在评估镜面BRDF frf_r时,我们将f90f_{90}设定为90°的方式如下:

f90=1.0f_{90}=1.0

图19显示了一个红色的塑料球。如果你仔细观察球体的边缘,你将能够注意到在斜射角处的无色散射反射。

image.png

图19:在接近光线的角度,镜面反射变得无色

导体

金属表面的镜面反射是彩色的。

f0=baseColormetallicf_0=baseColor⋅metallic

清单 12 显示了绝缘体和金属材料的 f0f_0 如何计算。它显示了在金属情况下,镜面反射的颜色是从基色派生的。

vec3 f0 = 0.16 * reflectance * reflectance * (1.0 - metallic) + baseColor * metallic;

清单 12:在GLSL中计算介质和金属材料的f0f_0

4.8.3.3 粗糙度重映射和夹紧

用户设置的粗糙度,在此称为 perceptualRoughness,将使用以下公式重新映射为感知线性范围:

α=perceptualRoughness2α=perceptualRoughness^2

图20显示了一种银色金属表面,其粗糙度逐渐增加(从0.0到1.0),使用未修改的粗糙度值(下方)和重新映射值(上方)。

image.png

图20:粗糙度重映射比较:感知线性粗糙度(顶部)和粗糙度(底部)

通过这种视觉比较,可以明显看出,重映射后的粗糙度更容易让艺术家和开发人员理解。如果没有进行这种重映射,闪亮的金属表面将被限制在非常小的范围内,介于 0.0 和 0.05 之间。

布伦特·伯利在他的演示中也做出了类似的观察 Burley12 。在尝试其他重新映射(例如立方和二次映射)之后,我们得出结论,这种简单的平方重新映射提供了视觉上令人满意和直觉的结果,同时对实时应用成本低廉。

最后但同样重要的是要注意,在运行时使用的粗糙度参数在各种计算中使用,其中有限的浮点精度可能成为问题。例如,在移动 GPU 上,mediump 精度浮点数通常实现为半浮点数(fp16)。

在 Filament 中的基于物理的渲染,当计算像 1perceptualRoughness4\frac{1}{perceptualRoughness^4} 这样的小值时,会出现问题(GGX 计算中的粗糙度平方)。可以表示为半浮点数的最小值是 2142^{-14}6.1×1056.1×10^{-5}。为了避免在不支持非规约数设备上出现除以 0 的情况,因此 1roughness4\frac{1}{roughness^4} 的结果不能低于 6.1×1056.1×10^{-5}。为此,我们必须将粗糙度夹紧到 0.089,这将给我们 6.274×1056.274×10^{-5}

应该避免非规格化数以防止性能下降。粗糙度也不能设置为0,以避免显而易见的0除。

由于我们也希望镜面高光具有最小尺寸(接近于0的粗糙度几乎创建了看不见的高光),因此我们应该在着色器中将粗糙度夹紧到一个安全范围。 这种夹紧的另一个好处是纠正可能出现在低粗糙度值时的镜面混叠问题。

Frostbite引擎将分析光的粗糙度限制在0.045以减少镜面混叠。这在使用单精度浮点数(fp32)时是可能的。

4.8.3.4 混合和分层

正如 [Burley12] 和 [Neubelt13] 所指出的,这个模型允许通过简单地插值不同的参数,对不同材料进行强大的混合。特别是,这可以使用简单的蒙版来叠加不同的材料。

例如,图21显示了工作室Ready at Dawn在《凡尔赛宫:1886》中如何使用材质混合和分层,从简单材质库(金、铜、木头、锈迹等)中创建复杂外观。

image.png

图 21:材质混合和分层。来源:Ready at Dawn Studios

材料的混合和分层有效地是材料模型各个参数的插值。图22展示了光亮金属铬和粗糙红色塑料之间的插值。虽然中间混合的材料在物理上几乎没有意义,但它们看起来是合理的。

image.png 图22:从闪亮铬(左)到粗糙红色塑料(右)的插值

4.8.3.5 制作基于物理的材料

一旦了解了基本颜色、金属质感、粗糙度和反射率四个主要参数的性质,设计基于物理的材料就相当容易。

我们提供了一份实用的图表/参考指南(useful chart/reference guide),帮助艺术家和开发人员打造他们自己的基于物理的材料。

image.png

制作基于物理的材料

此外,这里简要总结了如何使用我们的材质模型:

所有材料

基础颜色应该不包含光照信息,除了微小堵隔。

金属几乎是二进制值。纯导体的金属性值为 1,纯介质的金属性值为 0。您应该尽量使用接近 0 和 1 的值。中间值用于表面类型之间的过渡(例如金属到生锈)。

非金属材料

基础颜色代表反射的颜色,应为严格范围内的 sRGB 值,范围为 50-240(严格范围)或 30-240(宽容范围)。

金属度应近似为0或接近0。

反射率应设置为 127 sRGB(0.5 线性,4% 反射率)。不要使用低于 90 sRGB(0.35 线性,2% 反射率)的数值。

金属材料

基础颜色代表了高光颜色和反射率。使用亮度为 67% 到 100%(170-255 sRGB)的数值。氧化或脏污的金属应该使用比清洁金属更低的亮度,以考虑非金属成分。

金属度应为1或接近于1。

忽略反射率(从基础颜色计算)。

4.9 透明涂层模型

先前描述的标准材料模型非常适用于由单一层制成的各向同性表面。遗憾的是,多层材料相当常见,特别是在标准层之上有薄透明层的材料。这类材料的现实世界示例包括汽车油漆、苏打罐、漆木、亚克力等。

image.png

图23:蓝色金属表面在标准材料模型(左)和透明面漆模型(右)下的比较

清漆层可以通过添加第二个镜面反射分布函数来模拟为标准材质模型的扩展,这意味着需要评估第二个镜面反射分布函数。为了简化实现和参数化,清漆层将始终是各向同性和电介质。基础层可以是标准模型允许的任何材料(电介质或导体)。

由于入射光将穿过透明涂层,我们也必须考虑能量损失,如图24所示。然而,我们的模型不会模拟互反射和折射行为。

image.png

图24:清漆表面模型

4.9.1 透明外层镜面BRDF

清漆层将使用标准模型中使用的相同的 Cook-Torrance 微平面 BRDF 进行建模。由于清漆层始终是各向同性和介电质的,并且具有较低的粗糙度值(请参见第 4.9.3 节),因此我们可以选择更便宜的 DFG 术语,而不会显著牺牲视觉质量。

[Karis13a] 和 [Burley12] 中列出的术语调查显示,我们在标准模型中已经使用的菲涅尔和NDF术语在计算上不比其他术语更昂贵。[Kelemen01] 描述了一个可以替代我们的Smith-GGX能见度项的简化术语:

V(l,h)=14(lh)2V(l,h)=\frac{1}{4(l⋅h)^2}

如[ Heitz14 ]所示,这种掩蔽阴影函数并非基于物理,但其简单性使其非常适合实时渲染。

简而言之,我们的清漆BRDF是一个Cook-Torrance镜面微平面模型,具有GGX法线分布函数,Kelemen能见度函数和Schlick菲涅尔函数。第13节展示了GLSL实现是多么简单。

float V_Kelemen(float LoH) {
    return 0.25 / (LoH * LoH);
}

清单 13:在GLSL中实现Kelemen能见度项

关于菲涅尔项的说明

镜面 BRDF 的菲涅尔项需要 f0f_0,即法线入射角处的镜面反射率。这个参数可以从界面的折射率计算得出。我们假设我们的透明涂层是由聚氨酯制成,这是一种常用于涂料和清漆中的化合物,或类似的物质。空气-聚氨酯界面的折射率为1.5,从中我们可以推导出 f0f_0

f0(1.5)=(1.51)2(1.5+1)2=0.04f_0(1.5)=\frac{(1.5−1)^2}{(1.5+1)^2}=0.04

这对应于我们所知的与常见介电材料相关的Fresnel反射率为4%。

4.9.2 表面响应中的集成

因为我们必须考虑到清漆层的能量损失,我们可以从方程1重新构造BRDF如下:

f(v,l)=fd(v,l)(1Fc)+fr(v,l)(1Fc)+fc(v,l)f(v,l)=f_d(v,l)(1−F_c)+f_r(v,l)(1−F_c)+f_c(v,l)

其中 FcF_c 是透明面漆 BRDF 的菲涅尔项,fcf_c 是透明面漆 BRDF。

4.9.3 清漆参数化

透明涂料材料模型包括先前为标准材料模式定义的所有参数,以及表6中描述的两个参数。

参数定义
ClearCoat透明涂层的强度。介于0和1之间的标量。
ClearCoatRoughness透明涂层的视觉光滑度或粗糙度。在0和1之间的标量

表6:清漆模型参数

清漆粗糙参数与标准材质的粗糙参数类似地重新映射并夹紧。

图25和图26展示了清漆参数如何影响表面的外观。

image.png

图25: 清漆从0.0(左)变化到1.0(右),金属度设置为1.0,粗糙度为0.8

image.png

图26:清漆粗糙度从0.0(左)变化到1.0(右),金属度设置为1.0,粗糙度为0.8,清漆为1.0

在重新映射、参数化和整合到标准表面响应后,清漆材质模型的GLSL实现如列表14所示。

void BRDF(...) {
    // compute Fd and Fr from standard model

    // remapping and linearization of clear coat roughness
    clearCoatPerceptualRoughness = clamp(clearCoatPerceptualRoughness, 0.089, 1.0);
    clearCoatRoughness = clearCoatPerceptualRoughness * clearCoatPerceptualRoughness;

    // clear coat BRDF
    float  Dc = D_GGX(clearCoatRoughness, NoH);
    float  Vc = V_Kelemen(clearCoatRoughness, LoH);
    float  Fc = F_Schlick(0.04, LoH) * clearCoat; // clear coat strength
    float Frc = (Dc * Vc) * Fc;

    // account for energy loss in the base layer
    return color * ((Fd + Fr * (1.0 - Fc)) * (1.0 - Fc) + Frc);
}

清单14:清漆BRDF在GLSL中的实现

4.9.4 基础层修改

具有清漆层的存在意味着我们应重新计算f0f_0,因为它通常基于空气-材料界面。因此,基层需要根据清漆-材料界面而不是清漆-材料界面计算f0f_0

这可以通过从f0f_0 计算材料的折射率(IOR),然后根据新计算的IOR和清漆层(1.5)的IOR计算一个新的f0f_0 来实现。

首先,我们计算基础层的折射率:

IORbase=1+f01f0IOR_{base}=\frac{1+f_0}{1−f_0}

然后我们根据这个新的折射率计算新的f0f_0值:

f0base=(IORbase1.5IORbase+1.5)2f_0base=(\frac{IOR_{base}−1.5}{IOR_{base}+1.5})^2

由于透明涂层的折射率固定,我们可以合并这两个步骤以简化:

f0base=(15f0)2(5f0)2f_0base=\frac{(1−5\sqrt{f_0})^2}{(5−\sqrt{f_0})^2}

我们还应该根据透明涂层层的折射率修改基层的明显粗糙度,但这是我们目前选择留出的内容。

4.10 各向异性模型

先前描述的标准材料模型只能描述各向同性表面,即在所有方向上具有相同属性的表面。然而,许多现实世界材料,如刷过的金属,只能使用各向异性模型进行复制。

image.png

图27:各向同性材料对比(左)和各向异性材料(右)

4.10.1 各向异性镜面BRDF

先前描述的各向同性镜面BRDF可以修改为处理各向异性材料。Burley通过使用各向异性的GGX NDF来实现这一点:

Daniso(h,α)=1παtαb1((thαt)2+(bhαb)2+(nh)2)2D_{aniso}(h,α)=\frac{1}{πα_tα_b}\frac{1}{((\frac{t⋅h}{α_t})^2+(\frac{b⋅h}{α_b})^2+(n⋅h)^2)^2}

不幸的是,这个 NDF 依赖于两个额外的粗糙度术语,即αbα_b,即副切线方向的粗糙度,以及αtα_t,即切线方向的粗糙度。Neubelt 和 Pettineo [Neubelt13] 提出了一种方法,通过使用描述材料两个粗糙度值之间关系的各向异性参数,从αtα_t 推导出αbα_b

αt=αα_t=α
αb=lerp(0,α,1anisotropy)α_b=lerp(0,α,1−anisotropy)

在 [Burley12] 中定义的关系不同,提供了更愉悦和直观的结果,但成本略高:

αt=α10.9×anisotropyα_t=\frac{α}{1−\sqrt{0.9×anisotropy}}
αb=α10.9×anisotropyα_b=α\sqrt{1−0.9×anisotropy}

我们选择遵循 [Kulla17] 中描述的关系,因为它可以创建清晰的高光:

αt=α×(1+anisotropy)α_t=α×(1+anisotropy)
ab=α×(1anisotropy)a_b=α×(1−anisotropy)

请注意,除了法线方向之外,这个 NDF 还需要切线和副切线方向。由于这些方向已经在法线映射中被使用,因此提供它们可能不是问题。

列出了清单 15对应的实现

float at = max(roughness * (1.0 + anisotropy), 0.001);
float ab = max(roughness * (1.0 - anisotropy), 0.001);

float D_GGX_Anisotropic(float NoH, const vec3 h,
        const vec3 t, const vec3 b, float at, float ab) {
    float ToH = dot(t, h);
    float BoH = dot(b, h);
    float a2 = at * ab;
    highp vec3 v = vec3(ab * ToH, at * BoH, a2 * NoH);
    highp float v2 = dot(v, v);
    float w2 = a2 / v2;
    return a2 * w2 * w2 * (1.0 / PI);
}

清单 15:在GLSL中实现Burley的各向异性NDF

此外,Heitz14 提出了一种各向异性蒙版阴影函数,以匹配与高度相关的GGX分布。 通过使用可见性函数,蒙版阴影项可以得到很大简化:

G(v,l,h,α)=χ+(vh)χ+(lh)1+Λ(v)+Λ(l)G(v,l,h,α)=\frac{χ^+(v⋅h)χ^+(l⋅h)}{1+Λ(v)+Λ(l)}
Λ(m)=1+1+α02tan2(θm)2=1+1+α02(1cos2(θm))cos2(θm)2Λ(m)=\frac{−1+\sqrt{1+α^2_0tan^2(θm)}}{2}=\frac{−1+\sqrt{1+α^2_0\frac{(1−cos2(θm))}{cos2(θm)}}}{2}

其中:

α0=cos2(ϕ0)αx2+sin2(ϕ0)αy2α_0=cos^2(ϕ0)α^2_x+sin^2(ϕ0)α^2_y

在推导之后,我们得到:

Vaniso(nl,nv,α)=12((nl)Λv+(nv)Λl)V_{aniso}(n⋅l,n⋅v,α)=\frac{1}{2((n⋅l)Λ_v+(n⋅v)Λ_l)}
Λv=αt2(tv)2+αb2(bv)2+(nv)2Λ_v=\sqrt{α^2_t(t⋅v)^2+α^2_b(b⋅v)^2+(n⋅v)^2}
Λl=αt2(tl)2+αb2(bl)2+(nl)2Λ_l=\sqrt{α^2_t(t⋅l)^2+α^2_b(b⋅l)^2+(n⋅l)^2}

术语ΛvΛ_v对每个光都是相同的,如果需要,只能计算一次。 结果实现在列表16中描述。

float at = max(roughness * (1.0 + anisotropy), 0.001);
float ab = max(roughness * (1.0 - anisotropy), 0.001);

float V_SmithGGXCorrelated_Anisotropic(float at, float ab, float ToV, float BoV,
        float ToL, float BoL, float NoV, float NoL) {
    float lambdaV = NoL * length(vec3(at * ToV, ab * BoV, NoV));
    float lambdaL = NoV * length(vec3(at * ToL, ab * BoL, NoL));
    float v = 0.5 / (lambdaV + lambdaL);
    return saturateMediump(v);
}

清单16:在GLSL中实现各向异性能见函数

4.10.2 各向异性参数化

各向异性材料模型包括先前为标准材料模式定义的所有参数,再加上表7中描述的额外参数。

参数定义
Anisotropy各向异性的量。标量在-1和1之间

表7:各向异性模型参数

不需要进一步重映射。请注意,负值将使各向异性与副切线方向对齐,而不是切线方向。图28显示了各向异性参数如何影响粗糙金属表面的外观。

image.png

表7:各向异性模型参数

4.11 次表面模型

[TODO]

4.11.1 次表面镜面双向反射分布函数(BRDF)

[TODO]

4.11.2 表面下参数化

[TODO]

4.12 布料模型

之前描述的所有材料模型旨在模拟密集表面,无论是在宏观还是微观层面。 然而,衣服和织物通常由松散连接的线组成,可以吸收和散射入射光。 之前展示的微表面BRDF在重现布料性质方面表现不佳,因为它们基于的假设是表面由行为像完美镜子的随机凹槽组成。 与硬表面相比,衣料的特征是具有大范围衰减的较软镜面反射,并存在由正向/反向散射引起的毛茸照明。 有些织物还呈现两种色调的镜面颜色(例如天鹅绒)。

图29显示了传统微平面BRDF如何无法捕捉牛仔布样品的外观。 表面看起来僵硬(几乎像塑料),更像是一块油布而不是一件衣服。 该图还显示了由吸收和散射引起的较柔和的镜面叶对于忠实再现织物的重要性。

image.png

图29: 使用传统微平面BRDF(左)和我们的布料BRDF(右)渲染的牛仔布比较

天鹅绒是布料材质模型的一个有趣用例。如图30所示,这种类型的织物由于正向和逆向散射而表现出强烈的边缘照明。这些散射事件是由纤维在织物表面直立引起的。当入射光来自与视线方向相反的方向时,纤维将正向散射光线。同样地,当入射光来自与视线方向相同的方向时,纤维将反向散射光线。

image.png

图30:展示前向和后向散射的丝绒织物

由于纤维是灵活的,理论上我们应该模拟梳理表面的能力。虽然我们的模型不复制这种特征,但它确实模拟了可归因于纤维方向随机变化的可见正面镜面贡献。

值得注意的是,仍然有一些类型的织物最好使用硬表面材质模型来建模。例如,皮革、丝绸和缎子可以使用标准或各向异性材质模型重新创建。

4.12.1 布质镜面BRDF

我们使用的布质高光BRDF是由Ashikhmin和Premeze在[Ashikhmin07]中描述的改进的微平面BRDF。在他们的工作中,Ashikhmin和Premeze指出,分布项对BRDF的贡献最大,而遮蔽/掩模术语对其天鹅绒分布并不必要。分布项本身是一个倒置的高斯分布。这有助于实现模糊照明(正向和反向散射),同时添加偏移量以模拟正面镜面的贡献。所谓的天鹅绒NDF定义如下:

Dvelvet(v,h,α)=cnorm(1+4exp(cot2θhα2))D_{velvet}(v,h,α)=c_{norm}(1+4_{exp}(\frac{−cot^2θ_h}{α^2}))

这个 NDF 是同一作者在[ Ashikhmin00 ]中描述的 NDF 的变体,特别修改以包括一个偏移量(设置为1)和一个振幅(4)。在[ Neubelt13 ]中,Neubelt 和 Pettineo 提出了这个 NDF 的归一化版本:

Dvelvet(v,h,α)=1π(1+4α2)(1+4exp(cot2θhα2)sin4θh)D_{velvet}(v,h,α)=\frac{1}{π(1+4α^2)}(1+4\frac{exp(\frac{−cot^2θ_h}{α^2})}{sin^4θ_h})

对于完整的镜面BRDF,我们还遴选[Neubelt13],用更平滑的变体替换传统的分母:

fr(v,h,α)=Dvelvet(v,h,α)4(nl+nv(nl)(nv))f_r(v,h,α)=\frac{D_{velvet}(v,h,α)}{4(n⋅l+n⋅v−(n⋅l)(n⋅v))}

在列表 17 中展示了天鹅绒 NDF 的实现,经过优化,以适合半浮点格式,并避免计算昂贵的余切,而是依赖于三角恒等式。请注意,我们从这个 BRDF 中删去了菲涅尔成分。

float D_Ashikhmin(float roughness, float NoH) {
    // Ashikhmin 2007, "Distribution-based BRDFs"
	float a2 = roughness * roughness;
	float cos2h = NoH * NoH;
	float sin2h = max(1.0 - cos2h, 0.0078125); // 2^(-14/2), so sin2h^2 > 0 in fp16
	float sin4h = sin2h * sin2h;
	float cot2 = -cos2h / (a2 * sin2h);
	return 1.0 / (PI * (4.0 * a2 + 1.0) * sin4h) * (4.0 * exp(cot2) + sin4h);
}

清单17:在GLSL中实现Ashikhmin的绒布NDF

在 [ Estevez17 ] 这里,Estevez 和 Kulla 提出了一种不同的 NDF(称为“Charlie”光泽),它基于指数正弦而不是反转高斯。 几个原因使这个 NDF 受人喜爱:它的参数化更自然,更直观,提供了更柔和的外观,并且,正如方程中所示 49,其实现更简单:

D(m)=(2+1α)sin(θ)1α2πD(m)=\frac{(2+\frac{1}{α})sin(θ)^{\frac{1}{α}}}{2π}

[Estevez17]还提出了一个新的阴影项,但由于成本原因我们在这里省略了。相反,我们依靠[Neubelt13]的可视性项(在上面的方程式48中显示)。这个NDF的实现在清单18中展示,经过优化以适应半浮点格式。

float D_Charlie(float roughness, float NoH) {
    // Estevez and Kulla 2017, "Production Friendly Microfacet Sheen BRDF"
    float invAlpha  = 1.0 / roughness;
    float cos2h = NoH * NoH;
    float sin2h = max(1.0 - cos2h, 0.0078125); // 2^(-14/2), so sin2h^2 > 0 in fp16
    return (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI);
}

清单18:在GLSL中实现“Charlie”NDF

4.12.1.1 光彩色

为了更好地控制布料的外观,并使用户能够重新创建双色高光材质,我们引入了直接修改高光反射的功能。图 31 显示了使用我们称为“光泽颜色”的参数的示例。

image.png

图31:蓝色织物,无光泽(左边)和带光泽(右边)

4.12.1.2 布质漫反射双向反射分布函数

我们的布料材质模型仍然依赖于 Lambertian 漫反射 BRDF。但是,它稍微修改为能量保守(类似于我们的清漆材质模型的能量保守)并提供可选的次表面散射项。这个额外项并非基于物理,并可以用于模拟某些类型织物中的光散射、部分吸收和再辐射。

首先,这是不带可选次表面散射的漫射项。

fd(v,h)=cdiffπ(1F(v,h))f_d(v,h)=\frac{c_{diff}}{π}(1−F(v,h))

其中F(v,h)F(v, h)是方程48中布料镜面BRDF的菲涅尔项。在实践中,我们选择在漫反射部分中省略了1F(v,h)1 - F(v, h)项。效果有点微妙,我们认为不值得增加成本。

使用包裹式漫反射光照技术实现次表面散射,采用能量守恒的形式。

fd(v,h)=cdiffπ(1F(v,h))nl+w(1+w)2csubsurface+nlf_d(v,h)=\frac{c_{diff}}{π}(1−F(v,h))⟨\frac{n⋅l+w}{(1+w)^2}⟩⟨c_{subsurface}+n⋅l⟩

ww 是 0 到 1 之间的值,定义了漫反射光应该如何包围终端。为了避免引入另一个参数,我们固定 w=0.5w = 0.5。请注意,使用包覆漫射光照时,漫反射项不应该乘以 nln ⋅ l。这种廉价的次表面散射近似的效果可在图32中看到。

image.png

图32: 白色布料(左列)与具有褐色次表面散射的白色布料(右列)

我们布料BRDF的完整实现,包括光泽颜色和可选的次表面散射,可以在清单 19 中找到。

// specular BRDF
float D = distributionCloth(roughness, NoH);
float V = visibilityCloth(NoV, NoL);
vec3  F = sheenColor;
vec3 Fr = (D * V) * F;

// diffuse BRDF
float diffuse = diffuse(roughness, NoV, NoL, LoH);
#if defined(MATERIAL_HAS_SUBSURFACE_COLOR)
// energy conservative wrap diffuse
diffuse *= saturate((dot(n, light.l) + 0.5) / 2.25);
#endif
vec3 Fd = diffuse * pixel.diffuseColor;

#if defined(MATERIAL_HAS_SUBSURFACE_COLOR)
// cheap subsurface scatter
Fd *= saturate(subsurfaceColor + NoL);
vec3 color = Fd + Fr * NoL;
color *= (lightIntensity * lightAttenuation) * lightColor;
#else
vec3 color = Fd + Fr;
color *= (lightIntensity * lightAttenuation * NoL) * lightColor;
#endif

清单19:在GLSL中实现我们的布料BRDF

4.12.3 布料参数化

布料材质模型包含了标准材质模型中之前定义的所有参数,但不包括金属和反射。表8中描述的两个额外参数也是可用的。

参数定义
SheenColor光泽色调用于创建双色高光织物(默认为0.04以匹配标准反射率)
SubsurfaceColor布质漫反射双向反射分布函数中的物理渲染后通过材料散射和吸收的漫反射颜色调色

表8:布料模型参数

要创建类似天鹅绒的材质,可以将基色设置为黑色(或深色)。色度信息应改为设置在光泽颜色上。要创建更常见的面料如牛仔布、棉布等,使用基色作为色度,使用默认的光泽颜色或将光泽颜色设置为基色的亮度。