目录
一、节点功能概述
高度转法线节点(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 核心算法原理
高度转法线的本质是通过高度场的梯度计算表面法向量,数学步骤如下:
- 梯度计算:使用
ddx
和ddy
获取高度场在屏幕空间的偏导数 - 表面几何推导:通过叉乘和点积计算表面切向量和法向量
- 空间转换:根据 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 地形细节增强
场景:程序化地形法线生成
- 需求:从高度图生成法线,增强地形表面细节
- 实现:
hlsl
// 采样高度图 float height = tex2D(_HeightMap, uv).r; // 生成切线空间法线 float3 normal = Unity_NormalFromHeight_Tangent_float(height, _NormalStrength, positionWS, tangentToWorld, out float3 outNormal); // 应用到标准着色器 O.Normal = normal;
- 优势:无需额外法线贴图,减少纹理内存占用
4.2 角色动态皱纹
场景:面部表情皱纹实时生成
- 需求:根据骨骼变形动态生成皱纹法线
- 步骤:
- 骨骼动画驱动顶点高度变化
- 高度值输入节点生成动态法线
- 混合到基础法线贴图
- 代码示例:
csharp
// C#脚本传递动态高度 material.SetFloat("_DynamicHeight", facialExpressionIntensity);
4.3 流体表面法线
场景:水面波浪效果
- 实现:
- 使用噪声函数生成高度场
- 高度转法线节点生成波浪法线
- 结合反射探针实现真实水面效果
- 关键逻辑:
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.05 | Tangent | 自然地表凹凸 |
金属划痕 | 0.05-0.1 | Tangent | 锐利表面细节 |
全局水面 | 0.05-0.15 | World | 反射与光照正确匹配 |
5.2 性能优化
- 降低采样频率:
- 对静态高度场,预烘焙法线贴图替代实时计算
- 合并运算:
hlsl
// 优化前(多次调用) float3 normal1 = NormalFromHeight(height1, strength1); float3 normal2 = NormalFromHeight(height2, strength2); // 优化后(一次计算) float3 combinedNormal = CustomNormalFromHeight(height1, height2, strength1, strength2);
- 移动端优化:
- 限制 Strength≤0.05,减少导数计算开销
六、注意事项与常见问题
6.1 空间匹配问题
- 确保法线空间与着色器其他部分一致:
- 切线空间法线需与法线贴图空间匹配
- 世界空间法线需正确转换到光照空间
6.2 导数计算异常
- 当高度场变化剧烈时,可能出现法线抖动:
- 增加平滑滤波处理高度场
- 限制 Strength 最大值
6.3 精度问题
- 高度值范围建议归一化到 [0,1],避免导数计算溢出:
hlsl
float normalizedHeight = saturate(In); // 归一化高度值
七、总结与拓展应用
高度转法线节点通过数学推导实现了从一维高度到三维法线的转换,其核心价值在于:
- 灵活性:无需法线贴图即可生成细节,适合程序性内容
- 性能优势:纯计算实现,减少纹理采样和内存占用
- 动态适应性:支持实时高度变化驱动法线更新
拓展方向:
- 结合深度学习生成更复杂的高度 - 法线映射关系
- 在 VR 中通过手势数据动态生成物体表面法线
- 开发基于物理的高度 - 法线转换模型,模拟真实材质形变