ShaderGraph节点解析(十三):高度转法线节点(Normal From Height Node)详解

目录

一、节点功能概述

二、端口与参数详解

2.1 端口配置

2.2 控制参数

三、数学原理与代码解析

3.1 核心算法原理

3.2 切线空间模式代码解析

3.3 世界空间模式代码解析

四、应用场景与实战案例

4.1 地形细节增强

场景:程序化地形法线生成

4.2 角色动态皱纹

场景:面部表情皱纹实时生成

4.3 流体表面法线

场景:水面波浪效果

五、使用技巧与优化策略

5.1 参数调整指南

5.2 性能优化

六、注意事项与常见问题

6.1 空间匹配问题

6.2 导数计算异常

6.3 精度问题

七、总结与拓展应用


一、节点功能概述

高度转法线节点(Normal From Height Node)是 Unity Shader Graph 中用于从高度值生成法线向量的核心工具。它通过计算高度场的梯度信息,将一维高度值转换为三维法线向量,广泛应用于程序性材质生成、地形细节增强和动态变形效果。该节点的核心优势在于:

  • 一维到三维转换:仅需高度值即可生成法线,减少纹理采样开销
  • 双空间输出:支持切线空间(Tangent)和世界空间(World)法线输出
  • 物理精确计算:基于表面梯度的数学推导,确保法线方向的物理正确性

二、端口与参数详解

2.1 端口配置

端口名称方向数据类型描述
In输入Float高度值(通常来自高度图或噪声)
Strength输入Float法线强度(建议范围 0-0.1,真实世界单位)
Out输出Vector 3生成的法线向量

2.2 控制参数

参数名称类型选项描述
Output Space下拉菜单Tangent/World设置法线输出的坐标空间:切线空间适用于纹理叠加,世界空间适用于全局光照

三、数学原理与代码解析

3.1 核心算法原理

高度转法线的本质是通过高度场的梯度计算表面法向量,数学步骤如下:

  1. 梯度计算:使用ddxddy获取高度场在屏幕空间的偏导数
  2. 表面几何推导:通过叉乘和点积计算表面切向量和法向量
  3. 空间转换:根据 Output Space 参数转换法线到目标坐标系

3.2 切线空间模式代码解析

hlsl

void Unity_NormalFromHeight_Tangent_float(float In, float Strength, float3 Position, float3x3 TangentMatrix, out float3 Out)
{
    // 计算世界空间位置的导数(用于表面几何计算)
    float3 worldDerivativeX = ddx(Position);
    float3 worldDerivativeY = ddy(Position);

    // 计算叉积向量(辅助表面梯度计算)
    float3 crossX = cross(TangentMatrix[2].xyz, worldDerivativeX);
    float3 crossY = cross(worldDerivativeY, TangentMatrix[2].xyz);
    float d = dot(worldDerivativeX, crossY);
    float sgn = d < 0.0 ? (-1.0f) : 1.0f;
    float surface = sgn / max(0.000000000000001192093f, abs(d));

    // 计算高度场的梯度(ddx/ddy为屏幕空间导数)
    float dHdx = ddx(In);
    float dHdy = ddy(In);
    float3 surfGrad = surface * (dHdx*crossY + dHdy*crossX);
    
    // 生成切线空间法线并归一化
    Out = normalize(TangentMatrix[2].xyz - (Strength * surfGrad));
    // 转换到切线空间(仅Tangent模式)
    Out = TransformWorldToTangent(Out, TangentMatrix);
}

关键步骤解析

  • ddx/ddy(Position):获取世界空间位置的屏幕梯度,用于表面几何计算
  • TangentMatrix:切线空间转换矩阵,用于法线空间变换
  • surfGrad:表面梯度向量,由高度场导数和几何参数计算得到

3.3 世界空间模式代码解析

hlsl

void Unity_NormalFromHeight_World_float(float In, float Strength, float3 Position, float3x3 TangentMatrix, out float3 Out)
{
    // 与Tangent模式前半部分相同(世界空间几何计算)
    float3 worldDerivativeX = ddx(Position);
    float3 worldDerivativeY = ddy(Position);

    float3 crossX = cross(TangentMatrix[2].xyz, worldDerivativeX);
    float3 crossY = cross(worldDerivativeY, TangentMatrix[2].xyz);
    float d = dot(worldDerivativeX, crossY);
    float sgn = d < 0.0 ? (-1.0f) : 1.0f;
    float surface = sgn / max(0.000000000000001192093f, abs(d));

    float dHdx = ddx(In);
    float dHdy = ddy(In);
    float3 surfGrad = surface * (dHdx*crossY + dHdy*crossX);
    
    // 直接输出世界空间法线(无需转换)
    Out = normalize(TangentMatrix[2].xyz - (Strength * surfGrad));
}

空间转换差异

  • 切线空间模式通过TransformWorldToTangent将法线转换到切线空间
  • 世界空间模式直接输出世界空间法线,适用于全局光照计算

四、应用场景与实战案例

4.1 地形细节增强

场景:程序化地形法线生成
  1. 需求:从高度图生成法线,增强地形表面细节
  2. 实现

    hlsl

    // 采样高度图  
    float height = tex2D(_HeightMap, uv).r;
    // 生成切线空间法线  
    float3 normal = Unity_NormalFromHeight_Tangent_float(height, _NormalStrength, positionWS, tangentToWorld, out float3 outNormal);
    // 应用到标准着色器  
    O.Normal = normal;
    
  3. 优势:无需额外法线贴图,减少纹理内存占用

4.2 角色动态皱纹

场景:面部表情皱纹实时生成
  1. 需求:根据骨骼变形动态生成皱纹法线
  2. 步骤
    • 骨骼动画驱动顶点高度变化
    • 高度值输入节点生成动态法线
    • 混合到基础法线贴图
  3. 代码示例

    csharp

    // C#脚本传递动态高度  
    material.SetFloat("_DynamicHeight", facialExpressionIntensity);
    

4.3 流体表面法线

场景:水面波浪效果
  1. 实现
    • 使用噪声函数生成高度场
    • 高度转法线节点生成波浪法线
    • 结合反射探针实现真实水面效果
  2. 关键逻辑

    hlsl

    // 时间驱动噪声高度场  
    float noiseHeight = snoise(float2(_Time.y * 0.5, uv * 2)) * 0.1;
    // 生成世界空间法线(适应全局光照)  
    float3 waterNormal = Unity_NormalFromHeight_World_float(noiseHeight, 0.5, positionWS, tangentToWorld, out float3 normal);
    

五、使用技巧与优化策略

5.1 参数调整指南

场景需求Strength 建议值Output Space效果描述
地形细节0.01-0.05Tangent自然地表凹凸
金属划痕0.05-0.1Tangent锐利表面细节
全局水面0.05-0.15World反射与光照正确匹配

5.2 性能优化

  1. 降低采样频率
    • 对静态高度场,预烘焙法线贴图替代实时计算
  2. 合并运算

    hlsl

    // 优化前(多次调用)
    float3 normal1 = NormalFromHeight(height1, strength1);
    float3 normal2 = NormalFromHeight(height2, strength2);
    
    // 优化后(一次计算)
    float3 combinedNormal = CustomNormalFromHeight(height1, height2, strength1, strength2);
    
  3. 移动端优化
    • 限制 Strength≤0.05,减少导数计算开销

六、注意事项与常见问题

6.1 空间匹配问题

  • 确保法线空间与着色器其他部分一致:
    • 切线空间法线需与法线贴图空间匹配
    • 世界空间法线需正确转换到光照空间

6.2 导数计算异常

  • 当高度场变化剧烈时,可能出现法线抖动:
    1. 增加平滑滤波处理高度场
    2. 限制 Strength 最大值

6.3 精度问题

  • 高度值范围建议归一化到 [0,1],避免导数计算溢出:

    hlsl

    float normalizedHeight = saturate(In); // 归一化高度值
    

七、总结与拓展应用

高度转法线节点通过数学推导实现了从一维高度到三维法线的转换,其核心价值在于:

  1. 灵活性:无需法线贴图即可生成细节,适合程序性内容
  2. 性能优势:纯计算实现,减少纹理采样和内存占用
  3. 动态适应性:支持实时高度变化驱动法线更新

拓展方向

  • 结合深度学习生成更复杂的高度 - 法线映射关系
  • 在 VR 中通过手势数据动态生成物体表面法线
  • 开发基于物理的高度 - 法线转换模型,模拟真实材质形变
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小李也疯狂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值