在物理真实感渲染(PBR)的进化历程中,标准的单一微表面双向反射分布函数(Single-layer Microfacet BRDF,如标准 Cook-Torrance 模型)已经足以胜任绝大多数金属、岩石、皮革等常规材质。然而,当镜头对准汽车车漆(Car Paint)、打蜡的大理石地面、碳纤维复合材料或施有光滑釉层的古典瓷器时,单层模型立刻暴露出致命的物理破绽:这些物体表面天然具备双层物理反射结构。
底层是具有粗糙度、颜色甚至金属闪光的基底材质(Base Layer),而其上方覆着一层极其通透、微表面平整且折射率接近 $1.5$ 的透明保护漆层(Clearcoat Layer)。当阳光照射在车漆上时,你既能看到顶层清漆反射出的锐利清晰的高光倒影,又能透过漆层看到底层金属颗粒所漫射出的柔和色泽。
在前向渲染管线(Forward Rendering)中,为材质增加一个清漆计算仅仅是在像素着色器内部多执行几行高光公式。但在延迟渲染管线(Deferred Shading)中,这却是一个牵一发而动全身的系统级架构危机:延迟光照阶段的所有几何与材质参数完全依赖 G-Buffer 传递。
如果为了支持清漆材质,草率地在 G-Buffer 中额外增加一张渲染目标(RT),在 1080p 或 4K 分辨率下,每帧的显存带宽吞吐将瞬间暴涨 20% 到 30%,直接击穿移动端 GPU 的片上内存(GMEM)上限。如何在不增加任何额外 G-Buffer 附件、维持经典 128 位带宽预算的前提下,将清漆参数高保真地打包并在延迟光照阶段完成能量守恒解算,是一项极其考究的工程挑战。
一、双层清漆材质的物理光学模型与能量衰减
双层介质的光学传输必须遵循严格的能量守恒定律。入射光线 $L$ 在接触到物体表面时,首先经历清漆层的分光:
- 顶层清漆镜面反射(Clearcoat Specular):
清漆通常为各向同性的介质(如聚氨酯或树脂),其折射率通常固定为 $n = 1.5$。根据菲涅尔方程,垂直入射时的基准反射率固定为:
$$F_0^{\text{clearcoat}} = \left(\frac{1.5 - 1.0}{1.5 + 1.0}\right)^2 = 0.04$$
清漆层的高光项 $f_{\text{spec}}^{\text{cc}}$ 采用专用的 Cook-Torrance 高光模型,其粗糙度 $\alpha_{\text{cc}}$ 通常极低(在 $[0.01, 0.1]$ 区间),表现为极度聚集的锐利光芒。 - 能量折减与基底透射(Base Layer Attenuation):
光线穿透清漆层后,剩余的能量比例为 $(1 - F_{\text{cc}})$。这部分光线才会到达底层基底,与基底的漫反射与金属高光发生作用,并在出射时再次穿过清漆层受到 $(1 - F_{\text{cc}})$ 的衰减。因此,底层材质的总出射辐射率必须乘以透射因子:
$$f_{\text{combined}} = f_{\text{spec}}^{\text{cc}} \cdot \text{ClearcoatWeight} + f_{\text{base}} \cdot (1 - F_{\text{cc}} \cdot \text{ClearcoatWeight})^2$$
这种非线性的能量吸收保证了当清漆层反光极强时,底层的漫反射与暗色金属会被自然压制,呈现出如同浸润在琥珀中的通透厚重感。
二、128-Bit 极致紧凑 G-Buffer 压缩与通道打包黑科技
为了兼顾主流移动端(TBR/TBDR)与主机平台的带宽红线,我们必须将 G-Buffer 的总位宽死死锁定在 128 位(即 4 张 32 位的标准纹理):
- RT 0 (RGBA8_UNORM):
[Albedo.r, Albedo.g, Albedo.b, MaterialModelID (2-bit) + ShadingFlags (6-bit)] - RT 1 (RGBA8_UNORM):
[WorldNormal.x, WorldNormal.y (八面体编码), Roughness (8-bit), Metallic (8-bit)] - RT 2 (RGBA8_UNORM):
[Specular / Cavity (8-bit), ClearcoatStrength (8-bit), ClearcoatRoughness (8-bit), AmbientOcclusion (8-bit)] - RT 3 (R10G10B10A2 / RGBA8):
[Emissive.rgb, ClearcoatNormalDeviation (2-bit packed)] - 深度缓冲 (D24S8 / D32F): 硬件硬件原生深度,用于世界位置重建。
清漆参数的精密量化:
- 清漆强度(Clearcoat Strength):通过 8 位无符号归一化整数(
UNORM)存储,映射区间 $[0.0, 1.0]$; - 清漆粗糙度(Clearcoat Roughness):清漆层天然平整光滑,其物理粗糙度几乎不会超过 0.3。我们采用非线性平方映射:
$$\text{EncodedRoughness} = \sqrt{\frac{\alpha_{\text{cc}}}{0.3}}$$
在解码时执行平方还原。这一操作将有限的 8 位精度最大程度集中在极光滑($[0.01, 0.08]$)的超敏感高光过渡区,彻底消除了阶梯状高光断层; - 材质模型掩码(Material Model ID):在 RT 0 的 Alpha 通道前两位标记材质类型(
00: 默认 PBR,01: 双层清漆,10: 次表面散射,11: 各向异性头发),使延迟着色器能够根据片元动态分支跳过非清漆像素的昂贵解算。
三、HLSL 生产级着色器实现:从 G-Buffer 写入到延迟光照
以下为完整的 HLSL 着色器实现,包含 G-Buffer 几何 Pass 的打包压缩与光照 Pass 的能量守恒物理求解:
// G-Buffer 几何写入阶段 (GBuffer Pass Pixel Shader) struct GBufferOutput { float4 RT0 : SV_Target0; // Albedo + MaterialID float4 RT1 : SV_Target1; // Encoded Normal + Roughness + Metallic float4 RT2 : SV_Target2; // Cavity + ClearcoatStrength + ClearcoatRoughness + AO }; // 八面体法线压缩 (Octahedral Encoding) float2 EncodeOctahedralNormal(float3 n) { n /= (abs(n.x) + abs(n.y) + abs(n.z)); float2 enc = (n.z >= 0.0) ? n.xy : (1.0 - abs(n.yx)) * sign(n.xy); return enc * 0.5 + 0.5; } GBufferOutput PackClearcoatGBuffer( float3 albedo, float3 worldNormal, float roughness, float metallic, float clearcoatStrength, float clearcoatRoughness, float ao) { GBufferOutput output; // 1. 材质 ID 编码 (0x01 表示清漆材质) uint materialId = 1; // 2 bits output.RT0 = float4(albedo, (float)materialId / 3.0); // 2. 法线八面体压缩 + 粗糙度/金属度 float2 octNormal = EncodeOctahedralNormal(normalize(worldNormal)); output.RT1 = float4(octNormal, roughness, metallic); // 3. 清漆参数高精度量化 float encodedCCRoughness = sqrt(saturate(clearcoatRoughness / 0.3)); output.RT2 = float4(1.0, clearcoatStrength, encodedCCRoughness, ao); return output; } // ------------------------------------------------------------- // 延迟光照解算阶段 (Deferred Lighting Pass) // ------------------------------------------------------------- // 清漆专属微表面高光 D 项 (Kelemen 简化公式) float D_Clearcoat(float NdotH, float roughness) { float a2 = roughness * roughness; float d = (NdotH * a2 - NdotH) * NdotH + 1.0; return a2 / (3.14159265 * d * d + 1e-5); } // 清漆层高光 V 项 (专为光滑介质推导的轻量化几何遮挡) float V_Clearcoat(float NdotL, float NdotV) { return 0.25 / ((NdotL * (1.0 - NdotL) + NdotL) * (NdotV * (1.0 - NdotV) + NdotV) + 1e-5); } // 双层物理光照综合求值 float3 EvaluateClearcoatLighting( float3 baseColor, float metallic, float baseRoughness, float clearcoatStrength, float clearcoatRoughness, float3 N, float3 V, float3 L, float3 lightColor) { float3 H = normalize(V + L); float NdotL = saturate(dot(N, L)); float NdotV = saturate(dot(N, V)); float NdotH = saturate(dot(N, H)); float VdotH = saturate(dot(V, H)); if (NdotL <= 0.0 || NdotV <= 0.0) return float3(0.0, 0.0, 0.0); // 1. 解算底层基础材质 (标准 PBR) float3 F0_base = lerp(float3(0.04, 0.04, 0.04), baseColor, metallic); // 此处调用标准 GGX + Smith 评估底层光照 float3 baseLighting = EvaluateStandardPBR(baseColor, F0_base, baseRoughness, metallic, N, V, L); // 若无清漆,直接返回基底光照 if (clearcoatStrength <= 0.001) { return baseLighting * lightColor * NdotL; } // 2. 解算顶层清漆高光 (Clearcoat Specular) float D_cc = D_Clearcoat(NdotH, clearcoatRoughness); float V_cc = V_Clearcoat(NdotL, NdotV); // 清漆层折射率固定为 1.5,F0 = 0.04 float F_cc = 0.04 + (1.0 - 0.04) * pow(1.0 - VdotH, 5.0); float clearcoatSpecular = (D_cc * V_cc * F_cc) * clearcoatStrength; // 3. 能量折减因子:底层光照穿透清漆层的衰减比例 float layerAttenuation = 1.0 - F_cc * clearcoatStrength; // 4. 物理能量守恒双层合流 float3 compositeColor = (baseLighting * (layerAttenuation * layerAttenuation) + clearcoatSpecular) * lightColor * NdotL; return compositeColor; }四、运行时分支优化与 IBL 环境光多级重要性采样
在实际的延迟光照全屏 Quad 或 Compute Shader 遍历中,如果不加区分地对屏幕上所有像素执行清漆解算,会导致片元着色器内部算术逻辑单元(ALU)负载激增。
我们采用动态波前分支剔除(Wavefront / Subgroup Early Out):
- 材质模型早退:着色器首先解包 RT 0 中的
MaterialModelID。如果当前 8x8 像素块(SIMD Wave / Warp)内的所有像素均非清漆材质,GPU 分支预测器会瞬间整体跳过所有的清漆高光数学函数; - 环境光照(IBL)双层探针采样:
在计算天光漫反射与镜面反射时,基底材质采样经过基底粗糙度过滤的环境辐照度贴图,而清漆层则直接采样 Mip 0(原始最高清晰度)的环境反射贴图,并同样以 $(1 - F_{\text{cc}})^2$ 对环境漫反射进行暗化加权。
通过极其精妙的通道二进制量化、非线性感知粗糙度编码以及严格物理保真的能量衰减模型,延迟渲染管线得以在零额外显存带宽负担的前提下,将豪车漆面与晶莹玉石那层摄人心魄的通透质感,完美呈现在玩家眼前。