目录
一、节点功能概述
归一化节点(Normalize Node)是 Unity Shader Graph 中用于将向量长度缩放到单位长度(1)的基础数学工具。它接收动态向量(Dynamic Vector)作为输入,输出与输入方向相同但长度为 1 的向量。无论是二维向量(float2)、三维向量(float3)还是四维向量(float4),该节点都能自动计算向量的模长并按比例缩放各分量,适用于需要保持方向但统一长度的场景(如法线计算、光照方向处理、方向向量分析等)。其核心优势在于:
- 方向保持:严格维持输入向量的方向,仅调整长度
- 数学严谨性:基于标准向量运算实现,确保结果的物理正确性
- 多维度适配:自动处理不同维度的向量,无需手动编写缩放逻辑
二、端口详解
端口名称 | 方向 | 类型 | 描述 |
---|---|---|---|
In | 输入 | Dynamic Vector | 输入向量(可为 float2、float3 或 float4) |
Out | 输出 | Dynamic Vector | 归一化后的向量(长度为 1) |
三、技术原理解析
3.1 归一化运算的数学定义
3.2 生成代码解析
节点基于 HLSL 的normalize
函数实现向量归一化,示例代码如下(以 float4 输入为例):
hlsl
void Unity_Normalize_float4(float4 In, out float4 Out)
{
Out = normalize(In); // 计算归一化向量
}
- 零向量处理:当输入为零向量时,HLSL 的
normalize
函数返回零向量(而非 NaN),确保渲染稳定性。
3.3 几何意义
归一化向量在几何上表示方向,常用于表示空间中的朝向(如表面法线、光照方向)。例如:
- 表面法线向量归一化后,可确保光照计算中的点积运算正确反映光线与表面的夹角。
- 方向向量归一化后,可用于控制粒子发射方向或物体移动方向,避免因长度差异导致的速度变化。
四、应用场景与实战案例
4.1 法线计算与光照
场景:确保表面法线向量长度为 1,正确计算光照
- 需求:在顶点着色器中计算切线空间法线,并归一化以避免光照失真
- 实现步骤:
hlsl
// 计算切线空间法线(可能因变换导致长度非1) float3 normalWS = TransformObjectToWorldNormal(IN.normal); float3 tangentWS = TransformObjectToWorldDir(IN.tangent.xyz); float3 bitangentWS = cross(normalWS, tangentWS) * IN.tangent.w; // 归一化法线向量 float3 normalizedNormal = NormalizeNode.Out; // 输入为normalWS // 使用归一化后的法线计算光照 float ndotl = max(dot(normalizedNormal, _WorldSpaceLightPos0.xyz), 0.0); float3 diffuse = _LightColor0.rgb * ndotl;
4.2 方向引导效果
场景:基于归一化方向向量控制粒子运动或物体朝向
- 步骤:
hlsl
// 计算从物体到目标点的方向向量 float3 direction = _TargetPosition - IN.worldPos; // 归一化方向向量(确保速度一致) float3 normalizedDir = NormalizeNode.Out; // 输入为direction // 应用方向引导(如粒子移动) o.vertex.xyz += normalizedDir * _Speed * _Time.deltaTime;
4.3 向量投影与反射
场景:计算光线反射方向(需归一化入射方向和法线)
- 实现:
hlsl
// 归一化入射光线方向(假设未归一化) float3 lightDir = _WorldSpaceLightPos0.xyz; float3 normalizedLightDir = NormalizeNode.Out; // 输入为lightDir // 归一化表面法线 float3 normal = NormalizeNode.Out; // 输入为IN.normalWS // 计算反射方向(使用HLSL的reflect函数) float3 reflectDir = reflect(-normalizedLightDir, normal); // 应用反射方向(如高光计算) float3 viewDir = normalize(_WorldSpaceCameraPos - IN.worldPos); float specular = pow(max(dot(reflectDir, viewDir), 0.0), _Shininess);
4.4 程序化纹理生成
场景:基于归一化向量方向生成径向纹理效果
- 示例:
hlsl
// 计算从中心到UV的方向向量 float2 center = float2(0.5, 0.5); float2 direction = uv - center; // 归一化方向向量 float2 normalizedDir = NormalizeNode.Out; // 输入为direction // 基于方向向量生成纹理(如放射状条纹) float angle = atan2(normalizedDir.y, normalizedDir.x); float pattern = step(frac(angle * _Frequency), _Threshold); float3 finalColor = lerp(_ColorA, _ColorB, pattern);
五、使用技巧与注意事项
5.1 零向量处理
- 避免零向量输入:当输入为零向量时,归一化结果无意义(数学上未定义)。若可能存在零向量,需添加保护逻辑:
hlsl
// 方法1:使用safe_normalize函数(若存在) float3 safeNormal = length(In) > 0 ? normalize(In) : float3(0, 1, 0); // 方法2:手动判断 float3 normalized = In; float len = length(In); if (len > 0.0001) { // 小阈值避免浮点数精度问题 normalized = In / len; }
5.2 性能优化
- 减少冗余计算:若同一向量需多次归一化,建议缓存结果,避免重复计算。
- 顶点着色器与片段着色器:若向量在顶点着色器中已归一化,片段着色器中通常无需再次归一化(除非进行了非线性变换)。
5.3 与其他节点的配合
- 与长度节点配合:先计算向量长度(Length Node),再结合归一化向量,可分离方向与幅度信息。
- 与点积节点配合:归一化后的向量用于点积运算,确保结果仅反映角度关系(无长度影响)。
六、总结与拓展应用
归一化节点作为向量运算的基础工具,在光照计算、方向控制、几何处理等场景中不可或缺,其核心价值在于:
- 方向一致性:确保向量仅表示方向,消除长度差异带来的影响。
- 数学正确性:为光照模型、反射计算等提供严谨的数学基础。
- 性能保障:基于高效的 HLSL 内置函数,计算成本可控。
拓展方向:
- 结合归一化节点实现基于方向的材质变化(如表面朝向不同,颜色或纹理不同)。
- 在物理模拟中,使用归一化向量表示力的方向(如风力、重力)。
- 开发空间导航系统,通过归一化方向向量引导物体朝向目标点。