参考:https://zhuanlan.zhihu.com/p/375764857
线性混合 Linear Blending
描述:直接对两张法线贴图的向量相加后归一化。
核心思想:法线视为普通向量,线性叠加后重新单位化。
优缺点:
- ✅ 优点:实现简单,计算开销低(SM3.0仅需5条指令)。
- ❌ 缺点:导致法线“扁平化”,细节丢失严重;当一张为平面法线((0,0,1))时,结果偏向平均值而非保留另一张细节。
适用场景:对精度要求低的快速原型开发或低端设备。
代码实现:
float3 n1 = tex2D(texBase, uv).xyz * 2 - 1; // 解码法线
float3 n2 = tex2D(texDetail, uv).xyz * 2 - 1;
float3 r = normalize(n1 + n2); // 相加后归一化
return r * 0.5 + 0.5; // 编码回[0,1]范围叠加混合 Overlay Blending
描述:仿Photoshop叠加模式,独立处理RGB通道。
核心思想:根据基础法线亮度值选择乘法或屏幕混合。
优缺点:
- ✅ 优点:比线性混合保留更多对比度,美术可控性强。
- ❌ 缺点:无理论依据,混合后法线方向物理错误;计算开销较高(SM3.0需9条指令)。
适用场景:艺术导向的非PBR项目,如风格化渲染。
代码实现:
float3 n1 = tex2D(texBase, uv).xyz; // 未解码的RGB值
float3 n2 = tex2D(texDetail, uv).xyz;
// 叠加公式
float3 r = (n1 < 0.5) ? 2 * n1 * n2 : 1 - 2 * (1 - n1) * (1 - n2);
r = normalize(r * 2 - 1); // 解码并归一化
return r * 0.5 + 0.5;偏导数混合 Partial Derivative Blending
描述:通过法线计算表面偏导数(梯度),相加后再转回法线。
核心思想:法线本质是表面梯度,混合应在偏导数空间进行。
优缺点:
- ✅ 优点:数学基础扎实,平坦法线混合时结果正确。
- ❌ 缺点:高频细节易被平滑;需除法操作(
n.xy / n.z),性能较差,不过也有等效替代。 适用场景:高度图混合或材质渐变过渡(如地形融合)。
实现代码:
float3 n1 = tex2D(texBase, uv).xyz * 2 - 1; // 解码法线
float3 n2 = tex2D(texDetail, uv).xyz * 2 - 1;
// float3 r = normalize(float3(n1.xy/n1.z + n2.xy/n2.z, 1)); // 偏导
float3 r = normalize(float3(n1.xy * n2.z + n2.xy * n1.z, n1.z * n2.z)); // 避免显式除法:等效替代
return r * 0.5 + 0.5;Whiteout混合 Whiteout Blending
描述:SIGGRAPH 07提出,在偏导数混合基础上去除Z分量缩放。
核心思想:简化偏导数计算,保留更多高频细节。
优缺点:
- ✅ 优点:细节保留优于偏导数法;平坦法线混合结果正确。
- ❌ 缺点:仍存在轻微扁平化;指令数较高(SM3.0需7条)。
适用场景:需高频细节的场合(如布料褶皱叠加)。
代码实现:
float3 n1 = tex2D(texBase, uv).xyz * 2 - 1; // 解码法线
float3 n2 = tex2D(texDetail, uv).xyz * 2 - 1;
float3 r = normalize(float3(n1.xy + n2.xy, n1.z * n2.z)); // XY直接相加,Z相乘
return r * 0.5 + 0.5;UDN混合 Unreal Developer Network
描述:Whiteout的简化版,仅用基础法线的Z分量。
核心思想:牺牲少量细节换取性能提升。
优缺点:
- ✅ 优点:指令数更低(SM3.0约5条),性能友好。
- ❌ 缺点:基础法线被轻微压平,细节弱于Whiteout。
适用场景:主机/移动端性能敏感场景(如开放世界大 terrain)。
代码实现:
float3 n1 = tex2D(texBase, uv).xyz * 2 - 1; // 解码法线
float3 n2 = tex2D(texDetail, uv).xyz * 2 - 1;
float3 r = normalize(float3(n1.xy + n2.xy, n1.z)); // Z直接取自基础法线
return r * 0.5 + 0.5;重定向法线贴图 Reoriented Normal Mapping (RNM)
描述:将细节法线旋转至基础法线空间,保留两者强度。
核心思想:模拟切线空间变换,几何意义明确。
优缺点:
- ✅ 优点:细节保留最完整;平坦法线混合无副作用;数学严谨。
- ❌ 缺点:计算稍复杂(SM3.0需7-8条指令)。
适用场景:影视级渲染或PBR项目(如皮肤毛孔、高精度材质)。
四元数版本(主流引擎采用)
float3 t = tex2D(texBase, uv).xyz * float3(2, 2, 2) + float3(-1, -1, 0);
float3 u = tex2D(texDetail, uv).xyz * float3(-2, -2, 2) + float3(1, 1, -1);
float3 r = t * dot(t, u) / t.z - u; // 重定向计算
return normalize(r) * 0.5 + 0.5; // 需归一化Unity矩阵版本
float3 n1 = tex2D(texBase, uv).xyz * 2 - 1;
float3 n2 = tex2D(texDetail, uv).xyz * 2 - 1;
// 构造旋转矩阵
float3x3 nBasis = float3x3(
float3(n1.z, n1.y, -n1.x), // +Y旋转
float3(n1.x, n1.z, -n1.y), // -X旋转
float3(n1.x, n1.y, n1.z) // 原始法线
);
float3 r = normalize(n2.x * nBasis[0] + n2.y * nBasis[1] + n2.z * nBasis[2]);
return r * 0.5 + 0.5;提示
内置节点
RNM在Unity/UE中已有内置节点(如BlendAngleCorrectedNormals)
总结:方法选择建议
| 方法 | 细节保留 | 性能 | 物理正确性 | 典型用例 | |
|---|---|---|---|---|---|
| 线性/叠加混合 | ⭐ | ⭐⭐⭐⭐⭐ | ❌ | 低端设备快速实现 | |
| UDN混合 | ⭐⭐ | ⭐⭐⭐⭐ | ⚠️ | 主机/移动端开放世界 | |
| Whiteout混合 | ⭐⭐⭐ | ⭐⭐⭐ | ✅ | 高频细节叠加(布料/皱纹) | |
| RNM重定向 | ⭐⭐⭐⭐⭐ | ⭐⭐ | ✅ | 影视/PBR高精度渲染 |
提示
实践提示 混合前确保法线已解码到切线空间(-1~1),混合后根据需求决定是否归一化。