实时渲染中的 PBR 材质

2023-10-20 08:53:26 浏览数 (1)

基于物理的渲染(PBR,Physically Based Rendering)1 指在不同程度上基于现实世界的物理理论的渲染技术的集合。它包括了 2:

  • 基于物理的材质
  • 基于物理的光照
  • 基于物理的成像技术

相比与我们之前在图形学入门(三):基础着色中讨论的 Phong 和 Blinn-Phong 模型,使用 PBR 进行渲染的优势在于:

  • 真实:由于 PBR 采用了符合物理规律的方式模拟光线,因此总体上 PBR 的渲染结果看起来比非 PBR 的渲染结果更真实。
  • 直观:美术可以直接依据物理参数来编写 PBR 表面材质,而非使用各种模拟的方式调参使光照效果看起来正常。
  • 正确:无论光照条件如何,PBR 材质看上去都是正确的,而在非 PBR 的渲染中,我们需要根据光照情况来进行参数调整,才能使渲染结果真实可信。

事实上,PBR 在离线渲染中早已被广泛运用,我们看到的许多动画电影中逼真的渲染效果就运用了 PBR 技术。而由于计算量过大,PBR 长期没有在实时渲染领域发挥作用。随着运行平台的算力增强以及一系列优化算法的出现,PBR 现在已经成为高质量实时渲染中不可或缺的技术之一。出于运行性能的考虑,PBR 在实时渲染领域的渲染效果相对于离线渲染仍然有一定距离,而且部分技术依旧还只能在离线渲染领域使用。另外需要说明的一点是,虽然 PBR 是「基于物理」的技术,但出于性能考虑,在实现的时候会使用一些近似算法加速计算,这使得相关过程不太符合物理规律,但总体而言 PBR 仍然是以物理理论和模型作为指导的技术。在本文中,我们主要讨论基于物理的材质。

材质属性的描述 #

我们在深入理解渲染方程一文中详细讨论了渲染方程,这个方程从物理上正确描述了光在场景中流动。作为基于物理规律的渲染方式,PBR 本身也基于渲染方程。在讨论渲染方程的时候我们提到,渲染方程中决定物体表面材质属性的项是其中的 BRDF 项,因为 BRDF 描述了光如何在一个表面上被反射。

当一束光照射到物体表面时,光线的行进方向会主要分为两部分,一部分被折射(Refracted)进入到物体内部,而另一部分则被直接反射(Reflected)出来,如下图 3 所示:

进入到物体内部的光线会和物体内部的粒子进行作用,可能会被吸收(Absorbed),也可能会在物体内部散射(Scattered)。其中散射的光线可能会被消耗掉,也可能会重新离开物体,这部分离开物体的光方向会非常的分散,构成了物体表面的漫反射颜色。对于金属而言,折射光的能量会被自由电子立即吸收,也就是说,折射进金属表面的光并不会再出来,使得金属不会显示出漫反射的颜色。

光线和表面的这两种作用形式使得我们可以分别对其进行考虑。现在常用的 BRDF 模型是 Cook-Torrance BRDF。在这个模型里,BRDF 可以表示为 4:

f_r = k_d f_{lambert} k_s f_{cook-torrance}

其中,k_d 是漫反射能量所占的比例,即入射光线中进入物体表面的部分,k_s 则是和镜面反射能量的比例,即入射光中直接被反射的部分。根据能量守恒定律,我们知道出射光的能量无法超过入射光的能量,因此我们约束 k_d k_s le 1

另外需要说明的是,光线折射进入物体后,最终发出漫反射光只是一种简化的描述。光折射进部分材质(如玉石)后,会在内部散射然后从其他位置穿出物体,产生更加特殊的效果,类似这种现象叫做次表面散射(Subsurface Scattering)5。这种现象需要增加额外的计算才能模拟出来,本文暂不讨论这种复杂情况。

下面分别分析漫反射分量 f_{lambert} 和镜面反射分量 f_{cook-torrance}。我们从简单的部分开始,先来考虑其中的漫反射分量。

漫反射分量 #

为了了解我们如何表示漫反射的分量,我们可以构造一个理想的漫反射场景。假设物体表面上存在一个点,这个点的入射光在各个方向上是完全均匀的(即辐射率是均匀的)。且所有的入射光都折射进了物体表面,并最终全部均匀地离开物体表面,且这个物体本身不发光。

那么,由于物体不发光,我们可以将渲染方程简化为:

L_o(omega_o) = int_{H^2} f_r(omega_i rightarrow omega_o) L_i(omega_i) cos{}theta_i mathrm{d}omega_i

又由于它的入射光和出射光都在各个方向上均匀分布,BRDF 项 f_r 和入射光项 L_i 是和 omega 无关的,那么我们可以将 f_rL_i 直接从积分式中提出来:

L_o(omega_o) = f_r L_i int_{H^2} cos{}theta_i mathrm{d}omega_i

此时我们发现,这个方程的积分项就是对 cos{}theta 在半球上进行积分,这个积分的结果是 pi,因此我们知道:

L_o = f_r L_i pi

在没有能量损失的情况下,我们可知入射光和出射光的能量应该完全相等,也就是说 L_o = L_i,但是如果一个物体不是白色,那么只有一部分光被反射出来,剩下的能量会被吸收,我们通过一个反照率(Albedo)6 rho 来描述这个比例。这个 rho 可以是一个多通道的数值,例如对 RGB 三个通道分别有不同的吸收反射比例。那么,漫反射的 BRDF 可以表示为:

f_r = frac{rho}{pi}

这种理想漫反射模型被称为 Lambertian 反射 7,虽然现实中不存在这样理想的漫反射情况,但这个方法计算量很小,且能很好地近似模拟粗糙表面的效果,因此这个方法被广泛使用。

在了解了漫反射分量如何计算后,我们来讨论镜面反射的分量。

镜面反射分量 #

镜面反射分量比漫反射分量要复杂不少,它的计算方式如下:

f_r(mathrm{i}, mathrm{o}) = frac{F(mathrm{i}, mathrm{h}) G(mathrm{i}, mathrm{o}, mathrm{h}) D(mathrm{h})}{4 (mathrm{n}, mathrm{i}) (mathrm{n}, mathrm{o})}

其中,等式右侧的分母用于进行归一化,我们核心关注的点在分子部分。分子部分是三个项的乘积,它们分别为:

  • F 菲涅耳项(Fresnel Term)
  • G 阴影遮蔽项(Shadowing-Masking Term)
  • D 法线分布项(Normal Distribution Term)

下面我们分别讨论这几项。

菲涅耳项 #

菲涅耳项用于描述物体表面对光的反射比例和光的入射角(Incident Angle)以及光的极化情况(Polarization of Light)的关系。在下图 8 中我们可以看到,桌子在不同角度下对书本的反射情况存在明显的不同,当观察方向和桌面越接近平行,即入射角和桌面法线方向越接近 90^circ 的时候,光线反射比例越大,我们就能越清晰地看到桌面上书本的倒影(根据镜面反射定律,观察视线方向和桌面法线的夹角接近 90^circ 就意味着入射光与桌面法线的夹角接近 90^circ):

从下图 8 中我们能更加明确地看到反射比例和光的入射角之间的关系(图中的红线)。左侧的图是绝缘体的反射比例变化图,右侧则是导体的反射比例变化图,从图中可以看出绝缘体和导体的菲涅耳项有非常大的区别。其中,绝缘体的反射比例确实就像我们在上图中观察到的那样,与光的入射角度存在明显的正相关性。而导体的反射比例变化则较为复杂,有一个先降后升的过程。另外我们还能发现,相比与绝缘体,导体在任何角度下都有相当高的反射比例,我们日常也确实会发现金属总是会有相当明显的高光。

注意到在上图中除了红线以外还有两条曲线,这两条曲线反映的光的极化情况对反射比例的影响。光的极化又被称为「偏振」,这个性质与光的波动性有关 9。综合考虑两种极化情况来计算光的反射比例需要分别对图中蓝线和绿线的值求解,然后取平均得到红线的值,而且已经存在准确的公式用于进行求解 10。但这个公式非常复杂,在实际应用中我们会忽略极化性质产生的影响,并用一个更加简单的公式来进行计算。这被称为 Schlick’s 近似 11。Schlick’s 近似的核心思想在于将所有的菲涅耳项都拟合为一个较为简单的曲线,在 0^circ 的时候从某个基准反射率 R_0 开始,随着角度的增加逐步增长,在 90^circ 的时候渐变到 1。其公式如下:

begin{aligned} R(theta) &= R_0 (1 - R_0)(1 - cos{}theta)^5 newline R(0) &= left( frac{n_1 - n_2}{n_1 n_2} right)^2 end{aligned}

其中,公式中的 n_1n_2 分别是入射介质和折射介质的折射率。

这个近似公式中忽略了导体的菲涅耳项数值先下降再上升的过程,但导体的反射比例在不同角度下都比较高,这个近似效果是完全可以接受的。

菲涅耳项引出了一个问题,即我们既然认为菲涅耳项本身代表了光线被反射的比例,那么我们在前文中讨论的 BRDF 公式 f_r = k_d f_{lambert} k_s f_{cook-torrance} 中的 k_dk_s 又应该如何解释呢?

这事实上是工程上的一个不精准的近似方法,它存在的目的是为了补充计算时被我们忽略的能量,使物体的亮度不至于太暗。为了理解这个能量损失是如何产生的,我们要先理解 PBR 对物体表面建模的方式。PBR 对物体表面建模的方式是使用微平面模型(Microfacet Models)。

微平面模型 #

微平面模型认为物体的表面可以从宏观和微观两个维度来观察:

  • 从宏观角度而言,宏表面(Macrosurface)平坦但粗糙
  • 从微观角度而言,微表面(Microsurface)凹凸不平,而其中每个微平面则是光滑的

也就是说,这个模型认为当我们在足够近的距离去观察表面时,所有的表面都会展现出几何的性质,它们由很多很小的光滑镜面组成,这些小的光滑镜面被称为微平面(Microfacet)。每个微平面对入射光都会进行镜面反射。而当我们的观察距离拉远时,这些小型光滑镜面本身的几何性质会消失,其整体构成的材质属性会随之表现出来。如下图 8 所示:

在这个模型的认知下,如果物体表面的微平面法线方向比较集中,那么光线照上去后就会倾向于向接近的方向反射,那么物体表面就会产生较为明显的高光,也就是我们通常认为的较为「光滑」的表面,如下图 8 所示:

反之,如果物体表面的微平面法线方向分散,那么光线照上去后就会倾向于向不同方向反射,此时物体表面的高光就变得不明显,也就是我们通常认为的「粗糙」的表面,如下图 8 所示:

从上面的两幅渲染图中,我们也能看到能量守恒定律在此处起到的作用。物体表面法线方向越集中时,高光的亮度越高,但其范围越小,反之,物体表面法线方向越分散时,高光范围越大,但亮度则越低。

在微平面模型的指导下,我们知道,对于一个物体的表面而言,我们之所以能在某个方向上 omega_o 处看到高光,是因为有一部分微平面将入射光从 omega_i 反射到 omega_o。由于我们认为每一个微平面都是光滑的镜面,显然,能够将入射光完美反射到给定观察方向的微平面,就是法线方向和 omega_iomega_o 的半程向量相同的的微平面(关于半程向量的定义,我们在图形学入门(三):基础着色中已有讨论,这里不再赘述)。由于我们无法逐一表示如此多的微平面,因此我们选择用一个法线分布函数 D(mathrm{m}) 来描述物体表面的反射情况。

类似地,由于在微观上物体表面凹凸不平,因此这些凹凸的表面之间会发生相互的遮挡,如下图 12 所示。其中,光线在照射到物体表面时会产生阴影(Shadowing)(下图左),而光线在反射时则会被遮挡(Masking)无法被观察者看到(下图右):

我们用一个阴影遮蔽函数 G(mathrm{i}, mathrm{o}, mathrm{m}) 来描述这种遮挡关系造成反射结果变化。这里的 G 是几何(Geometry)的意思。

我们前面提到,高光项的计算产生了一些能量损失,因此我们加了一个漫反射项来补充了这部分能量。这部分损失的能量主要就是由阴影遮蔽产生的。我们在计算阴影遮蔽时,只要微平面间产生了互遮挡,我们就不考虑这部分能量了。但我们知道,光在现实的物体表面间会发生弹射,即便光线被表面遮挡,其中的部分能量还是可能在多次弹射后被观察者看到。而这部分本应被看到却被忽略的能量就是导致渲染结果变暗的原因,由于粗糙的物体表面更可能发生微平面的互遮挡,因此在渲染粗糙表面时,渲染效果的变暗情况会更加严重。虽然加入补充的能量的做法并不符合物理规律,但加入它能近似出不错的渲染效果。

在了解了微平面模型之后,下面我们就来详细讨论基于微平面模型导出的法线分布项和阴影遮蔽项具体怎么算。

法线分布项 #

法线分布项 D(mathrm{m}) 是一个密度函数,用于描述方向为 mathrm{m} 的法线在微平面上的统计分布情况。给定一个以方向 mathrm{m} 为中心的无穷小立体角 mathrm{d} omega_mathrm{m} 以及一个无穷小的宏表面 mathrm{d}AD(mathrm{m}) mathrm{d} omega_mathrm{m} mathrm{d}A 表示 mathrm{d}A 中法线位于 domega_mathrm{m} 立体角内的所有微平面的总面积。现在常用的法线分布函数为 Beckmann 模型 13 和 GGX 模型(又称 Trowbridge-Reitz 模型)14。

其中 Beckmann 模型的公式如下 14:

D(mathrm{m}) = frac{chi^ (mathrm{m} cdot mathrm{n})}{pi alpha^2 cos^4 theta_mathrm{m}}mathrm{e}^{- frac{tan^2 theta_{mathrm{m}}}{alpha^2}}

其中,

$$chi^ (x) = begin{cases} 1, & where x > 0 newline 0, & otherwise end{cases}$$

alpha 是表面的粗糙程度(alpha 越小则表面越光滑),mathrm{n} 是宏表面的法线,theta_mathrm{m} 则表示向量 mathrm{m}mathrm{n} 之间的夹角。这个公式相当复杂,但是它基本上是一个类似高斯分布的钟形曲线,区别在于这个函数定义在坡度空间(Slope Space),即其中的 tan^2 theta_mathrm{m} 部分。这里之所以不直接用 theta_mathrm{m} 而需要加入一个 tan 是因为 theta_mathrm{m} 的取值只有 [0, 90^circ],而高斯分布的定义域事实上是无穷大的,为了避免丢失这部分的能量,我们需要将变量的取值范围扩大,这里采用 tan 的目的就在此。我们知道,tan thetatheta = 0 时值为 0,而随着 theta 增大到 90^circ 时会趋于无穷大,这就符合了我们的要求。

Beckmann 模型类似高斯分布,那它自然也继承了高斯分布的一些性质。我们知道,高斯分布有一个三 sigma 法则(Three-Sigma Rule of Thumb)15,即尽管高斯分布的定义域是无穷大的,但在取值超过 pm 3 sigma 的时候,其值会变得非常小(pm 3 sigma 范围内占据了整个钟形曲线超过 99.7% 的面积)。这导致了基于 Beckmann 模型渲染的高光会出现边缘亮度衰减过快的感觉。

GGX 模型解决了 Beckmann 模型的这个问题,其公式如下 14:

D(mathrm{m}) = frac{alpha^2 chi^ (mathrm{m} cdot mathrm{n})}{pi cos^4 theta_mathrm{m} (alpha^2 tan^2 theta_mathrm{m})^2}

下图 14 对比了 Beckmann 模型和 GGX 模型变化趋势的差别(alpha = 0.2)。从中我们可以看出 GGX 模型(绿色曲线)相比 Beckmann 模型(红色曲线)存在明显的长尾,即在超过一定的范围后,Beckmann 模型的能量已经几乎衰减到 0,而 GGX 模型则仍然有较为明显的能量存在:

GGX 的长尾特性使得 GGX 能显著减缓高光边缘亮度的衰减。在 GGX 模型出现之前,为了模拟这样的效果,需要叠加多个 BRDF,而改用 GGX 之后,则只需要用一个 BRDF 即可实现了。下图 12 对比了 Beckmann 模型和 GGX 模型的实际渲染效果,可以看到 GGX 能渲染出更加柔和自然的高光:

阴影遮蔽项 #

在讨论阴影遮蔽项 G(mathrm{i}, mathrm{o}, mathrm{m}) 的计算之前,我们先来考虑一下这一项存在的意义。如果我们正对着看向场景中的一个球体,我们会看到一个圆。根据菲涅耳项的变化规律,我们知道,靠近圆心位置的表面法线和观察方向的夹角会接近于 0^circ,此时反射比例应该是基准反射率 R_0,而靠近边缘位置的表面法线方向和观察方向的夹角就比较接近 90^circ,反射比例应该是 1,因此我们理论上应该看到类似下图 4 中的边缘发光效果:

这样的发光效果显然与现实情况不相符,阴影遮蔽项存在的主要目的是为了中和菲涅耳项产生的这个影响。而这一项本身的定义也符合物理规律,它表达的是微表面在入射方向(阴影)和出射方向(遮蔽)的可见概率。我们可以想象,在微平面模型下,观察方向和表面法线的夹角越是接近 90^circ,那么阴影和遮蔽的情况必然更加明显,而当我们垂直看向一个表面时,阴影遮蔽项应该几乎不产生作用。一个常用的阴影遮蔽函数为 Smith 遮蔽函数。Smith 遮蔽函数为了简化计算,将阴影项和遮蔽项分开考虑(事实上这两项本身应该是有关联的):

G(mathrm{i}, mathrm{o}, mathrm{m}) approx G_1(mathrm{i}, mathrm{m}) G_1(mathrm{o}, mathrm{m})

这里的 G_1 有如下的基本形式 16:

G_1(mathrm{v}, mathrm{m}) = chi^ {left( frac{mathrm{v} cdot mathrm{m}}{mathrm{v} cdot mathrm{n}} right)} frac{1}{1 Lambda(mathrm{v})}

这里的 chi^ 和法线分布项中的定义相同。

阴影遮蔽的情况和法线分布情况存在明显的相关性,物体表面越粗糙,那么这个表面的微平面间的自遮挡情况就会越严重。因此 G_1 函数的选择与法线分布函数有关。Beckmann 模型对应的 G_1 具体为 14:

G_1(mathrm{v}, mathrm{m}) = chi^ {left( frac{mathrm{v} cdot mathrm{m}}{mathrm{v} cdot mathrm{n}} right)} frac{2}{1 mathrm{erf}(a) frac{1}{a sqrt{pi}} mathrm{e}^{-a^2}}

其中 mathrm{erf} 是误差函数,其公式为:

mathrm{erf}(x) = frac{2}{sqrt{pi}} int_{0}^{x} mathrm{e}^{-x^2} mathrm{d}x

这个公式非常复杂,因此一般会使用如下的方法近似 14:

$$G_1(mathrm{v}, mathrm{m}) approx chi^ {left( frac{mathrm{v} cdot mathrm{m}}{mathrm{v} cdot mathrm{n}} right)} begin{cases} frac{3.535a 2.181a^2}{1 2.276a 2.577a^2}, & if a < 1.6 newline 1, & otherwise end{cases}$$

其中,

a = (alpha tan{theta_v})^{-1}

这里的 alpha 就是法线分布项中的 alpha 参数,theta_vmathrm{v} 和宏表面法线 mathrm{n} 之间的夹角。

相较于 Beckmann 模型,GGX 模型对应的 G_1 就简单许多了,其公式具体为 14:

G_1(mathrm{v}, mathrm{m}) = chi^ {left( frac{mathrm{v} cdot mathrm{m}}{mathrm{v} cdot mathrm{n}} right)} frac{2}{1 sqrt{1 a^2 tan^2{theta_v}}}

下图 14 展示了两种模型对应的 G_1 的区别(alpha = 0.2),其中绿色的曲线是 Beckmann 模型对应的 G_1,红色曲线是 GGX 模型对应的 G_1。可以看到这两条曲线的形状和前面我们对阴影遮蔽项的分析相符合,在大多数角度下,G_1(mathrm{v}) 的值都几乎为 1,而当角度开始接近 pm 90^circ 时,G_1(mathrm{v}) 的值会急剧衰减,避免了前面提到的边缘发光的问题。

总结 #

将我们前面研究的 BRDF 代入反射方程,即可得出完整的 Cook-Torrance PBR 反射方程了:

L_o(mathrm{x}, omega_o) = int_{H^2} left( k_d frac{rho}{pi} k_s frac{F G D}{4 (mathrm{n}, mathrm{i}) (mathrm{n}, mathrm{o})} right) L_i(mathrm{x}, omega_i) cos{theta_i} mathrm{d}omega_i

在理解了渲染方程的物理基础以及 PBR BRDF 中各项的意义后,原本相当劝退的公式也就变得易于理解了。在实际工程中,PBR 材质的实现可能会有不同程度的修改,但基本形式是不变的。

参考资料 #

  1. PBR - Wikipedia ↩︎
  2. Moving Frostbite to PBR - S´ebastien Lagarde, Charles de Rousiers ↩︎
  3. An End-to-End Approach to Physically Based Rendering - Wes McDermott, Sam Bugden ↩︎
  4. 理论 - PBR - LearnOpenGL CN ↩︎ ↩︎
  5. 次表面散射 - Wikipedia ↩︎
  6. 反照率 - Wikipedia ↩︎
  7. Lambertian reflectance - Wikipedia ↩︎
  8. Materials and Appearances slide - Games 101 ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
  9. 偏振 - Wikipedia ↩︎
  10. 菲涅耳方程 - Wikipedia ↩︎
  11. Schlick’s approximation - Wikipedia ↩︎
  12. Real-Time Physically-Based Materials - Games 202 ↩︎ ↩︎
  13. Specular highlight - Wikipedia ↩︎
  14. Microfacet Models for Refraction through Rough Surfaces - Bruce Walter ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
  15. 68–95–99.7 原则 - Wikipedia ↩︎
  16. Physically Based Rendering - Matt Pharr ↩︎

0 人点赞