讀書筆記:OpenPBR 規范(2)
3. 模型
? ?????使用前述的公式和參數化方法,我們現在來具體說明 OpenPBR 表面模型的結構。我們首先描述“非薄壁”情況(“薄壁”情況下的結構有所不同),其材質結構非正式地如下圖所示:

? ?????總而言之,其公式結構由以下層構成:

?????這些層通過以下方式組合成材質結構(下文記為 $ M_{\text{PBR}} $):
$$
M_{\text{PBR}} = \textbf{mix}(S_{\text{ambient-medium}}, M_{\text{surface}}, \alpha)\quad\text{where}\,\alpha = \text{geometry opacity}
$$
$$
M_{\text{surface}} = \textbf{layer}(M_{\text{coated-base}}, S_{\text{fuzz}}, F)\quad\text{where}\,F = \text{fuzz weight}
$$
$$
M_{\text{coated-base}} = \textbf{layer}(M_{\text{base-substrate}}, S_{\text{coat}}, C)\quad\text{where}\,C = \text{coat weight}
$$
$$
M_{\text{base-substrate}} = \textbf{mix}(M_{\text{dielectric-base}}, S_{\text{metal}}, M)\quad\text{where}\,M = \text{base metalness}
$$
$$
M_{\text{dielectric-base}} = \textbf{mix}(M_{\text{opaque-base}}, S_{\text{translucent-base}}, T)\quad\text{where}\,T = \text{transmission weight}
$$
$$
M_{\text{opaque-base}} = \textbf{mix}(M_{\text{glossy-diffuse}}, S_{\text{subsurface}}, S)\quad\text{where}\,S = \text{subsurface weight}
$$
$$
M_{\text{glossy-diffuse}} = \textbf{layer}(S_{\text{diffuse}}, S_{\text{gloss}})
$$
?????其結構呈現為由 layer 和 mix 操作生成的樹形形式:

? ?????除了上述模型結構中明確列出的權重(weight)和不透明度(opacity)參數外,每個組件層(slab)的屬性還由下文詳述的更多參數控制(完整參數集請參閱“參數參考”部分)。
? 代碼分析如下:
? ?????a. 首先保存光照方向、相機方向、法向方向、片元相機空間坐標、遮蔽系數等參數
? ?????b. 然后調用mx_sheen_bsdf函數,輸入fuzz_weight、fuzz_color, fuzz_roughness, geometry_normal,輸出fuzz_bsdf_out
BSDF fuzz_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_sheen_bsdf(closureData, fuzz_weight, fuzz_color, fuzz_roughness, geometry_normal, 1, fuzz_bsdf_out);
? ?????c. 然后調用mx_dielectric_bsdf函數,輸入weight, tint, ior, roughness, thinfilm_thickness, thinfilm_ior, N, X, distribution, scatter_mode,輸出coat_bsdf_out
BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_dielectric_bsdf(closureData, coat_weight, vec3(1.000000, 1.000000, 1.000000), coat_ior, coat_roughness_vector_out, 0.000000, 1.500000, geometry_coat_normal, geometry_coat_tangent, 0, 0, coat_bsdf_out);
? ?????d. 然后調用mx_generalized_schlick_bsdf函數,輸入weight, color0, color82, color90, exponent, roughness, thinfilm_thickness, thinfilm_ior, N, X, distribution, scatter_mode,輸出metal_bsdf_out
BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_generalized_schlick_bsdf(closureData, specular_weight_tf_inv_out, metal_reflectivity_out, metal_edgecolor_out, vec3(1.000000, 1.000000, 1.000000), 5.000000, main_roughness_out, 0.000000, 1.500000, geometry_normal, geometry_tangent, 0, 0, metal_bsdf_out);
? ?????e. 然后調用mx_generalized_schlick_bsdf函數,輸入weight, color0, color82, color90, exponent, roughness, thinfilm_thickness, thinfilm_ior, N, X, distribution, scatter_mode,輸出metal_bsdf_tf_out
? ?????再使用mx_add_bsdf將metal_bsdf_out, metal_bsdf_tf_out加起來得到metal_bsdf_tf_blend_out
BSDF metal_bsdf_tf_out = BSDF(vec3(0.0),vec3(1.0));
mx_generalized_schlick_bsdf(closureData, specular_weight_tf_out, metal_reflectivity_out, metal_edgecolor_out, vec3(1.000000, 1.000000, 1.000000), 5.000000, main_roughness_out, thin_film_thickness_nm_out, thin_film_ior, geometry_normal, geometry_tangent, 0, 0, metal_bsdf_tf_out);
BSDF metal_bsdf_tf_blend_out = BSDF(vec3(0.0),vec3(1.0));
mx_add_bsdf(closureData, metal_bsdf_out, metal_bsdf_tf_out, metal_bsdf_tf_blend_out);
? ?????f. 然后調用mx_dielectric_bsdf函數,輸入weight, tint, ior, roughness, thinfilm_thickness, thinfilm_ior, N, X, distribution, scatter_mode,輸出dielectric_reflection_out
BSDF dielectric_reflection_out = BSDF(vec3(0.0),vec3(1.0));
mx_dielectric_bsdf(closureData, thin_film_weight_inv_out, specular_color, modulated_eta_s_out, main_roughness_out, 0.000000, 1.500000, geometry_normal, geometry_tangent, 0, 0, dielectric_reflection_out);
? ?????調用mx_dielectric_bsdf函數,輸入weight, tint, ior, roughness, thinfilm_thickness, thinfilm_ior, N, X, distribution, scatter_mode,輸出dielectric_reflection_tf_out。同樣使用mx_add_bsdf函數得到dielectric_reflection_blend_out
BSDF dielectric_reflection_tf_out = BSDF(vec3(0.0),vec3(1.0));
mx_dielectric_bsdf(closureData, thin_film_weight, specular_color, modulated_eta_s_out, main_roughness_out, thin_film_thickness_nm_out, thin_film_ior, geometry_normal, geometry_tangent, 0, 0, dielectric_reflection_tf_out);
BSDF dielectric_reflection_blend_out = BSDF(vec3(0.0),vec3(1.0));
mx_add_bsdf(closureData, dielectric_reflection_out, dielectric_reflection_tf_out, dielectric_reflection_blend_out);
? ?????g. 調用mx_dielectric_bsdf函數,輸入weight, tint, ior, roughness, thinfilm_thickness, thinfilm_ior, N, X, distribution, scatter_mode,輸出dielectric_transmission_out
BSDF dielectric_transmission_out = BSDF(vec3(0.0),vec3(1.0));
mx_dielectric_bsdf(closureData, 1.000000, if_transmission_tint_out, modulated_eta_s_out, main_roughness_out, 0.000000, 1.500000, geometry_normal, geometry_tangent, 0, 1, dielectric_transmission_out);
? ?????調用mx_anisotropic_vdf函數,輸入absorption, scattering, anisotropy,輸出dielectric_volume_out。再調用mx_layer_vdf函數混合dielectric_transmission_out, dielectric_volume_out得到dielectric_volume_transmission_out
BSDF dielectric_volume_out = BSDF(vec3(0.0),vec3(1.0));
mx_anisotropic_vdf(closureData, if_volume_absorption_out, if_volume_scattering_out, transmission_scatter_anisotropy, dielectric_volume_out);
BSDF dielectric_volume_transmission_out = BSDF(vec3(0.0),vec3(1.0));
mx_layer_vdf(closureData, dielectric_transmission_out, dielectric_volume_out, dielectric_volume_transmission_out);
? ?????h. 然后調用mx_oren_nayar_diffuse_bsdf函數,輸入weight, color, roughness, N, energy_compensation,輸出subsurface_thin_walled_reflection_bsdf_out,乘上系數得到subsurface_thin_walled_reflection_out
BSDF subsurface_thin_walled_reflection_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_oren_nayar_diffuse_bsdf(closureData, 1.000000, subsurface_color_nonnegative_out, base_diffuse_roughness, geometry_normal, false, subsurface_thin_walled_reflection_bsdf_out);
BSDF subsurface_thin_walled_reflection_out = BSDF(vec3(0.0),vec3(1.0));
mx_multiply_bsdf_color3(closureData, subsurface_thin_walled_reflection_bsdf_out, subsurface_thin_walled_brdf_factor_out, subsurface_thin_walled_reflection_out);
? ?????i. 然后調用mx_translucent_bsdf函數,輸入weight, color, N,輸出subsurface_thin_walled_transmission_bsdf_out,乘上系數得到subsurface_thin_walled_transmission_out。使用mx_mix_bsdf函數混合上一步值得到subsurface_thin_walled_out
BSDF subsurface_thin_walled_transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_translucent_bsdf(closureData, 1.000000, subsurface_color_nonnegative_out, geometry_normal, subsurface_thin_walled_transmission_bsdf_out);
BSDF subsurface_thin_walled_transmission_out = BSDF(vec3(0.0),vec3(1.0));
mx_multiply_bsdf_color3(closureData, subsurface_thin_walled_transmission_bsdf_out, subsurface_thin_walled_btdf_factor_out, subsurface_thin_walled_transmission_out);
BSDF subsurface_thin_walled_out = BSDF(vec3(0.0),vec3(1.0));
mx_mix_bsdf(closureData, subsurface_thin_walled_reflection_out, subsurface_thin_walled_transmission_out, 0.500000, subsurface_thin_walled_out);
? ?????j. 然后調用mx_subsurface_bsdf函數,輸入weight, color, radius, anisotropy, N,輸出subsurface_bsdf_out。再使用mx_mix_bsdf函數混合subsurface_thin_walled_out, subsurface_bsdf_out得到selected_subsurface_out
BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_subsurface_bsdf(closureData, 1.000000, subsurface_color_nonnegative_out, subsurface_radius_scaled_out, subsurface_scatter_anisotropy, geometry_normal, subsurface_bsdf_out);
BSDF selected_subsurface_out = BSDF(vec3(0.0),vec3(1.0));
mx_mix_bsdf(closureData, subsurface_thin_walled_out, subsurface_bsdf_out, subsurface_selector_out, selected_subsurface_out);
? ?????k. 然后調用mx_oren_nayar_diffuse_bsdf函數,輸入weight, color, roughness, N, energy_compensation,輸出diffuse_bsdf_out。再混合selected_subsurface_out得到opaque_base_out。再混合dielectric_volume_transmission_out得到dielectric_substrate_out。再調用mx_layer_bsdf函數得到dielectric_base_out。
BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_oren_nayar_diffuse_bsdf(closureData, base_weight, base_color_nonnegative_out, base_diffuse_roughness, geometry_normal, true, diffuse_bsdf_out);
BSDF opaque_base_out = BSDF(vec3(0.0),vec3(1.0));
mx_mix_bsdf(closureData, selected_subsurface_out, diffuse_bsdf_out, subsurface_weight, opaque_base_out);
BSDF dielectric_substrate_out = BSDF(vec3(0.0),vec3(1.0));
mx_mix_bsdf(closureData, dielectric_volume_transmission_out, opaque_base_out, transmission_weight, dielectric_substrate_out);
BSDF dielectric_base_out = BSDF(vec3(0.0),vec3(1.0));
mx_layer_bsdf(closureData, dielectric_reflection_blend_out, dielectric_substrate_out, dielectric_base_out);
? ?????m. 最后混合metal_bsdf_tf_blend_out和dielectric_base_out得到base_substrate_out。乘以darkened_base_substrate_out得到darkened_base_substrate_out。乘以coat_attenuation_out得到coat_substrate_attenuated_out。調用mx_layer_bsdf函數得到coat_layer_out。調用mx_layer_bsdf函數得到fuzz_layer_out
// Calculate the BSDF response for this light source
BSDF base_substrate_out = BSDF(vec3(0.0),vec3(1.0));
mx_mix_bsdf(closureData, metal_bsdf_tf_blend_out, dielectric_base_out, base_metalness, base_substrate_out);
BSDF darkened_base_substrate_out = BSDF(vec3(0.0),vec3(1.0));
mx_multiply_bsdf_color3(closureData, base_substrate_out, modulated_base_darkening_out, darkened_base_substrate_out);
BSDF coat_substrate_attenuated_out = BSDF(vec3(0.0),vec3(1.0));
mx_multiply_bsdf_color3(closureData, darkened_base_substrate_out, coat_attenuation_out, coat_substrate_attenuated_out);
BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0));
mx_layer_bsdf(closureData, coat_bsdf_out, coat_substrate_attenuated_out, coat_layer_out);
BSDF fuzz_layer_out = BSDF(vec3(0.0),vec3(1.0));
mx_layer_bsdf(closureData, fuzz_bsdf_out, coat_layer_out, fuzz_layer_out);
? ?????由于該模型本質上只是對材質結構的物理描述,因此原則上它適用于通過精確方法求解,例如[Jakob2014]、[Belcour2018]和[Zeltner2018]中開發的方法。這些方法試圖計算光在整個層堆棧中所有不同的反射和透射模式,最終生成一個未必是各界面BSDF簡單線性組合的最終BSDF。然而,我們希望此材質模型能夠在廣泛的渲染平臺上使用,從離線路徑追蹤器一直到移動設備上的實時游戲引擎。強制規定特定的實現方式會使該材質模型在某些類別的渲染器中不實用,并最終降低模型的實用性。因此,我們將最終BSDF的具體實現方式的選擇視為本規范范圍之外的事項。
? ?????為了方便和效率,目前該模型最有可能被映射到一個由多種BSDF波瓣(lobe)混合組成的模型,類似于Autodesk Standard Surface著色器[Georgiev2019]及其在MaterialX中的表示。下文“簡化為波瓣混合模型”部分將提供一個此類模型的推導示例。我們還基于此推導提供了一個MaterialX的參考實現。
? ?????接下來,我們將討論此結構中各層的BSDF和介質的具體形式。
3.1 微平面模型
? ?????此處我們針對上一節所述模型中描述界面的各 BSDF 的形式和參數化給出一些通用假設。
? ?????金屬、電介質、涂層和光澤漫反射板層的 BSDF,即 $ f_{\text{conductor}} $, $ f_{\text{dielectric}} $, $ f_{\text{coat}} $ 和 $ f_{\text{diffuse}} $,均假定由標準微平面模型描述。這是一種廣泛使用的近似方法 [Pharr2023],其假設表面由光滑的金屬、電介質或朗伯材質的微平面構成的高度場組成,這些面元的法線(稱為微法線)的統計分布決定了宏觀尺度的表面粗糙度特性。(絨毛模型則不同,它基于體積"微薄片"模型 [Heitz2015])。
? ?????在單次散射近似下,微平面 BRDF 具有如下標準形式? [Walter2007], [Pharr2023]:
? ?????其中 $ h=(\omega_{i}+\omega_{o})/|\omega_{i}+\omega_{o}| $ 是半角向量,即能將 $ \omega_{i} $ 鏡面反射至 $ \omega_{o} $ 的微法線。對于電介質,還存在 BTDF(即 BSDF 中輸入和輸出方向分別位于相對半球而非同一半球的部分),其形式與 BRDF 相似,但具有修正的半角向量和菲涅爾因子 [Walter2007]。
? ?????菲涅爾因子 $ F(\omega_{i},h) $ 由每個微平面的反射材料的復折射率(IOR)決定(更專業地說,由其與外部 IOR 的比值決定),其形式因材料是電介質還是導體而異 [Walter2007]。每種情況下的參數化將在"電介質基底"章節和"金屬"章節中描述。
? ?????遮蔽陰影函數 $ G(\omega_{i},\omega_{o}) $ 解釋了輸入和輸出方向被微平面遮擋的概率。它通常使用給定 NDF 后計算 $ G $ 的 Smith 模型推導得出,對于 GGX NDF(公式 18),其遮蔽陰影函數有一個眾所周知的形式 [Heitz2014]。
? ?????法線分布函數(NDF)$ D(m) $ 描述了表面上微法線 $ m $ 出現的相對概率,從而決定了粗糙度特性。一種能很好近似真實材料粗糙度且廣為流行的 NDF 是所謂的 GGX 分布(此名稱源于"毛玻璃",但該公式最初由 Trowbridge 和 Reitz 提出 [Walter2007], [Burley2012], [Heitz2014], [Pharr2023]),其基本形式? 為:
? ?????其中 $ \theta_{m} $ 是 $ m $ 與(宏觀)表面法線之間的夾角,參數 $ \alpha $ 控制微平面的表觀粗糙度。當 $ \alpha\to 0 $ 時,法線分布高度集中于 $ \theta_{m}=0 $ 附近,因此微平面大多平坦,呈現光滑外觀;而當 $ \alpha $ 增大時,微平面變得越來越粗糙,呈現粗糙外觀。實際上,我們將 $ \alpha $ 限制在范圍 $ [0,1] $ 內,因為 $ \alpha>1 $ 不會產生 plausible 的粗糙外觀。遵循 [Burley2012],我們設定(在各向同性情況下):
? ?????其中 $ r\in[0,1] $ 是面向用戶的粗糙度參數,因為這樣能在改變 r 時,使表觀粗糙度的變化在感知上更線性。
? ?????在一般情況下,粗糙度是各向異性的,即法線分布函數(NDF)并非呈圓對稱,而是沿著表面平面內的某個方向拉伸,導致高光沿該方向拉長。這模擬了由劃痕或刷磨等工藝產生的連貫微觀溝槽幾何結構。我們假設已定義了一個參考切向量場(通過 geometry_tangent 和 geometry_coat_tangent 定義)。該參考切向量指示了 NDF 被拉伸的方向,意味著微觀溝槽傾向于與副法線方向正交。
? ?????隨后,各向異性情況下的 GGX 分布通過沿切向量和副法線方向的兩個獨立的粗糙度參數 $ \alpha_t $ 和 $ \alpha_b $ 進行參數化,具體形式如下(給定微法線 $ m $ 相對于切向量的極角 $ \phi_m $(以法線為軸逆時針測量)):
? ?????當 $ \alpha_t=\alpha_b=\alpha $ 時,此公式簡化為各向同性形式。文獻 [Heitz2018] 和 [Dupuy2023] 中提出了對采用各向異性 GGX 微平面模型的 BSDF 進行高效采樣的技術。NDF 參數 $ \alpha_t $ 和 $ \alpha_b $ 可以更方便地參數化為總粗糙度 $ r $ 和各向異性程度 $ a\in[0,1] $。我們建議使用以下從 $ r \(、\) a $ 到 $ \alpha_t \(、\) \alpha_b $ 的映射關系:
? ?????該公式滿足 \(\alpha_t^2+\alpha_b^2=2\alpha^2\),以保持平均粗糙度不隨各向異性程度變化。其原理是,如果渲染器不支持各向異性(或者出于性能考慮,例如細節級別控制,而關閉了該功能),僅使用粗糙度參數應能產生一個在感知上與原始各向異性高光相近的各向同性高光。圖 2 顯示了所生成高光的形狀(從技術上講,這些是 NDF $ D_{\mathrm{GGX}}(m) $ 在二維斜率空間中的等高線)。

? ???????????????圖 2:NDF形狀隨粗糙度r和各向異性程度a的變化關系
?????總結NDF參數化設定如下:電介質基底BSDF $ f_{\text{dielectric}} $ 和金屬基底BRDF $ f_{\text{conductor}} $ 共享相同的參數,而涂層BSDF $ f_{\text{coat}} $ 使用獨立的一組參數:
- specular_roughness(用于 $ f_{\text{dielectric}} $ 和 $ f_{\text{conductor}} $)與 coat_roughness(用于 $ f_{\text{coat}} $)定義了粗糙度參數 r。這些粗糙度被限制在 [0, 1] 范圍內,因為 r = 1 對應感知上非常粗糙的表面。
- specular_roughness_anisotropy(用于 $ f_{\text{dielectric}} $ 和 $ f_{\text{conductor}} $)與 coat_roughness_anisotropy(用于 $ f_{\text{coat}} $)指定了 a ∈ [0, 1](即NDF沿局部表面切向方向拉伸的程度)。然后,根據公式 (21) 確定最終的NDF參數 $ \alpha_{t} $ 和 $ \alpha_{b} $。
? ?????需要注意的是,對于方向相反的切向量,高光的視覺效果是相同的;這也有利于在與其他支持方向各向異性的模型之間進行轉換時保持數值的有效性。
? ?????公式 (17) 所描述的單次散射微面BRDF不滿足能量守恒,因為它忽略了微面之間的多次散射。理想的實現方案應通過某種方法來解決此問題,否則粗糙金屬和電介質的反射會顯得比應有的更暗且飽和度更低。目前已有多種方案:
?????* [Heitz2016a] 中描述了一種完全精確的方法,通過蒙特卡洛方法顯式模擬多次反射。
?????* [Kulla2017] 提出了更簡化的近似模型,通過添加補償波瓣來解釋損失的能量。
?????* [Turquin2019] 提出了另一種近似模型,通過縮放波瓣的反照率來維持能量守恒,但犧牲了互易性。

浙公網安備 33010602011771號