【基于物理的渲染(2)】Cook-Torrance

【基于物理的渲染(2)】Cook-Torrance

BRDF

BRDF 直译为双向散射分布函数,是一种解释光反射强度的模型。双向散射是指其将反射光描述为由镜射光和漫射光两种。基于物理的 BRDF 说明该公式不能是传统的经验模型,而是遵循各种物理现象而设计的模型。

漫射光

漫射光是指光线射入物体内部,在内部多次反弹后返回的光,这类光容易被物体吸收,且反射的方向无序,因此反光将均匀的分布在照射区域。

镜射光

镜射光是指光线仅触碰表面一次就反射的光,这种光与多种系数有关,例如表面的光滑度,观察的角度等,故光斑可变,且有可能发生一定的全反射(光未被表面吸收)。

Cook-Torrance

CookTorrance 是最常用的基于物理的 BDRF,相比过去的一些经验模型,它的效果更加真实,符合物理规律。CookTorrance 中有很多过去 Lambert 和 Phong 的影子,可以说就是由它们为基础慢慢发展出的新时代光照模型,也是目前主流的光照模型。

微表面属性

从Phong开始就有了考虑不同表面光泽度而显示不同高光的效果,Cook-Torrance也不例外,用其计算光照,需先提供一些微表面属性信息。

  • albedo(反射率):决定了物体表面对不同波段光的反射率(不被吸收率,也可简单理解为物体表面颜色)。
  • metallic(金属度):控制漫射光和镜射光之间的比例,以及镜射光颜色。金属度越高,漫射越少,镜射越多,镜射颜色越接近物体表面颜色。
  • smoothness(光滑度):物体表面的光滑程度,用于控制镜射光的形状特征。

基本公式结构

基本的 CookTorrrance 光照模型结构如下:

\[CookTorrance = k_d + k_s * f_s \]

  • \(k_d\):表面对每单位漫射光的反射率。
  • \(k_s\):表面对每单位镜射光的反射率。
  • \(f_s\):镜射系数,表示有多少光会被直接反射进眼睛。

反射率计算

根据能量守恒 \(k_d+k_s=1\),而两者之间的权重分配则采用金属度系数决定:

\[k_d = lerp(1,0,metallic) \\ k_s = lerp(0,1,metallic) \]

但现实中的一般物体是不可能全漫射的,即使是黑色表面,遇到强光也会反射出镜射光,因此需要提供一定的保底全反射,该全反射的系数一般为 0.04(标准介电镜面反射):

\[k_d = lerp(0.96,0,metallic) \\ k_s = lerp(0.04,1,metallic) \]

反射光的光不一定和入射光颜色一样,因为很多物体的表面也会吸收部分波段的光,因此计算时还需要考虑表面的基础反照率(表面颜色):

\[k_d = lerp(0.96 * albedo,0,metallic) \\ k_s = lerp(0.04,1 * albedo,metallic) \]

镜射系数计算

镜射系数的计算公式如下:

\[f_s = \frac{D*G*F}{4(N \cdot L)(N \cdot V)} \]

  • \(N\):法线方向
  • \(L\):光源方向
  • \(V\):相机方向

在部分资料中该公式的分母系数 \((N \cdot L_m)_(N \cdot V)\) 可能会被去除,因为这部分可以被 \(G\) 中的分子约分。但如果不去除的话,可以确保 \(G \in [0,1]\),直观上看,这样会更能表现公式的物理效果。

其中 D、G、F 是三个可插拔的系数,用于反应 3 种物理现象。可插拔意味着它们是可以更换的,有多种计算方式可供选择,因此可以以此实现渲染效果的定制。

此外 D、G 的计算依赖一个新的系数叫粗糙度,粗糙度可以通过物体表面的光滑度计算得出:

\[a = (1-smoothness)^2 \]

法线分布

D 描述表面法线分布情况,具体而言它决定了镜面高光的光斑形状。下述是使用 GGX 计算 D 的方法:

  • \(H=\operatorname{normalize}(L+V)\):半角向量的缩写。

\[D_{GGX} = \frac{a^2}{\pi((H\cdot N)^2(a^2-1)+1)^2} \]

其中 \(\pi\) 是为了归一化,使其光亮的总和符合能量守恒,但有些地方(如 Unity)会故意去掉该项。

几何遮蔽

G 用于表现微表面之间的遮挡导致的光线衰减现象,这在粗糙的表面尤为明显。下述是使用 Smith 计算 G 的方法:

\[G_{Smith} = \frac{(N \cdot L)}{lerp(k,1,N \cdot L)} *\frac{(N \cdot V)}{lerp(k,1,N \cdot V)} \]

\[k=\frac{(a+1)^2}{8}(直接光) 或 \frac{a^2}{2}(间接光) \]

从中可以观察到:

  • 光线入射角或观察角度与表面越垂直,光线被遮挡的概率越小。
  • 越光滑的表面,光线被遮挡的概率越小。
  • 对于直接光始终会被吸收一部分,而环境光可以全反射。

此外还有一种效果接近,但性能更好的方法:

\[G_{SKSm}=\frac{(N \cdot L)(N \cdot V)}{lerp(a,1,(L \cdot H)^2)} \]

菲涅尔效应

F 表示一个 0-1 的光线反射率,反应的是现实中的菲涅尔效应,描述了不同折射率的物体在不同观察角度下反射光线不同的特点。一般它的公式采用如下近似值:

\[F = lerp(F_0,1,(1-(V \cdot N))^5) \]

\[F_0 = \frac{(r-1)^2}{(r+1)^2} \]

  • \(F_0\):法向反射率。
  • \(r\):折射率。

从中可以观察到以下几种现象:

  • 折射率越高,菲涅尔效应越明显(如水面)。
  • 菲涅尔效应发生时,物体正面反射光线少(能透过湖面看到底),掠角反射光线多(湖面变不透明)。
  • 折射率为 0,没有菲涅尔效应,发生全反射。

不过上述菲涅尔公式只能用于透明物体,因为它会使物体表面反射为 0。对于不透明物体,可以需要采用另一种系数计算方法:

\[F_0 = k_s \]

不过该方法将菲涅尔函数的输出由标量改为了向量,且携带了镜射光的反射率信息,因此要修改 CookTorrance 公式,使镜射光部分不要再乘 \(k_s\) 了(\(k_d+k_sf_s \to k_d+f_s\))。

Unity 中的镜射系数计算

https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_renaldas_2D00_notes.pdf

Unity 中对于镜射系数的计算公式如下:

\[\begin{aligned} D &= \frac{a^2}{((H \cdot N)^2(a^2-1)+1)^2} \\ \\ G*F &= \frac{1}{\operatorname{max}((L \cdot H)^2,0.1)(a+0.5)}\\ \\ f_s &= \frac{D * G * F}{4} \end{aligned} \]

其中 \(D\) 就是 \(D_{GGX}\) 方法,但去掉了用于归一化的 \(\pi\) ,使画面更亮。

\(G*F\) 是拟合的近似公式。在原始公式中,\(G\) 使用的是 \(G_{SKSm}\),但 \(F\) 的公式不清楚,因为原始公式和最终公式的效果并不相同。

\((N \cdot L)(N \cdot V)\)被从分子分母中约分以加快速度。

参考资料

posted @ 2025-12-23 17:03  BDFFZI  阅读(2)  评论(0)    收藏  举报