【节点】[Distance节点]原理解析与实际应用
Distance 节点是 Unity URP Shader Graph 中的一个重要数学运算节点,它计算两个输入向量之间的欧几里德距离。欧几里德距离是几何学中最常见的距离度量方式,表示在 n 维空间中两点之间的直线距离。
在计算机图形学和着色器编程中,Distance 节点具有广泛的应用场景。它不仅用于基本的距离计算,还是实现各种高级视觉效果的基础工具。该节点特别适用于计算有符号距离函数(Signed Distance Function,SDF),这是现代实时渲染中用于描述几何形状边界的重要数学工具。
Distance 节点的工作原理基于欧几里德距离公式。对于二维空间中的两点 A(x₁, y₁) 和 B(x₂, y₂),距离计算公式为:√[(x₂-x₁)² + (y₂-y₁)²]。对于三维空间,公式扩展为:√[(x₂-x₁)² + (y₂-y₁)² + (z₂-z₁)²]。Shader Graph 中的 Distance 节点自动处理这些计算,支持从一维到四维的向量输入。
该节点在视觉效果创作中的重要性体现在多个方面:
- 它是创建基于距离的渐变、过渡效果的基础
- 用于实现物体边缘发光、轮廓检测等效果
- 在程序化生成内容中用于形状描述和空间划分
- 是许多高级渲染技术如距离场渲染、光线步进的基础构建块
数学原理
欧几里德距离基础
欧几里德距离是衡量空间中两点间"直线"距离的标准方法。在着色器编程中理解其数学原理对于有效使用 Distance 节点至关重要。
对于不同维度的向量,距离计算略有不同:
- 一维向量:Distance = |A - B|
- 二维向量:Distance = √[(A.x - B.x)² + (A.y - B.y)²]
- 三维向量:Distance = √[(A.x - B.x)² + (A.y - B.y)² + (A.z - B.z)²]
- 四维向量:Distance = √[(A.x - B.x)² + (A.y - B.y)² + (A.z - B.z)² + (A.w - B.w)²]
在 Shader Graph 中,无论输入向量的维度如何,Distance 节点始终输出一个浮点数值,表示两个输入向量之间的绝对距离。
距离计算的实际考虑
在实际着色器应用中,出于性能考虑,有时会使用距离的平方而不是实际距离。这是因为平方根计算在 GPU 上相对昂贵。Distance 节点内部确实计算了完整的欧几里德距离,包括平方根操作,但在某些性能敏感的场景中,开发者可能会选择手动计算平方距离来避免平方根开销。
例如,当只需要比较距离大小时(如找出最近的点),使用平方距离就足够了,因为平方根函数是单调递增的,距离的大小关系与平方距离的大小关系一致。
端口详解
![]()
输入端口
A 端口
- 方向:输入
- 类型:动态矢量(Float,Vector2,Vector3,Vector4)
- 描述:第一个输入向量,代表空间中的一个点或位置。这个端口可以接受不同维度的向量输入,根据连接的数据类型自动适应。在实际应用中,A 端口通常代表需要计算距离的起始点或参考点。
B 端口
- 方向:输入
- 类型:动态矢量(Float,Vector2,Vector3,Vector4)
- 描述:第二个输入向量,代表空间中的另一个点或位置。与 A 端口一样,B 端口也支持动态向量类型,但必须与 A 端口保持相同的维度。B 端口通常代表目标点或需要测量距离的终点。
输出端口
Out 端口
- 方向:输出
- 类型:Float
- 描述:输出 A 和 B 之间的欧几里德距离,始终为标量值。无论输入向量的维度如何,输出都是单个浮点数,表示两点之间的绝对距离。这个值总是非负的,因为距离没有方向性。
端口连接规范
Distance 节点对输入端口有一些重要的连接要求:
- A 和 B 端口必须连接相同维度的向量类型
- 如果连接不同维度的向量,Shader Graph 会显示编译错误
- 输入端口支持直接连接常量值、属性、其他节点的输出或图形输入节点
- 输出端口可以连接到任何接受浮点数输入的端口
使用方法和技巧
基本连接方法
使用 Distance 节点的基本步骤很简单:
- 将需要计算距离的两个向量分别连接到 A 和 B 端口
- 将 Out 端口连接到需要使用距离值的后续节点
- 根据需要调整后续节点的处理逻辑
典型的基本设置包括:
- 连接两个 Position 节点来计算空间中两点的距离
- 连接 UV 坐标和固定点来计算基于纹理坐标的距离
- 连接时间动画的向量来创建动态距离效果
性能优化技巧
虽然 Distance 节点使用方便,但在性能关键的场景中需要考虑优化:
- 在片段着色器中频繁使用 Distance 节点可能影响性能,特别是移动平台
- 对于只需要距离比较的场景,考虑使用点积运算手动计算平方距离
- 在顶点着色器中预计算距离然后插值到片段着色器可以提高性能
- 对于静态场景,考虑将距离计算烘焙到纹理中
常见应用模式
Distance 节点在着色器创作中有几种经典的应用模式:
径向渐变模式:
- 使用 Distance 节点计算当前片段到中心点的距离
- 将距离值映射到 0-1 范围作为渐变系数
- 使用渐变系数混合颜色或透明度
边缘检测模式:
- 计算到边界或特定位置的距离
- 使用步进或平滑步进函数创建清晰的边缘
- 可以用于创建描边、发光边界等效果
距离场渲染模式:
- 使用 Distance 节点计算到多个物体的距离
- 通过距离函数组合创建复杂形状
- 利用有符号距离函数实现高级几何渲染
实际应用案例
案例一:创建径向渐变着色器
径向渐变是 Distance 节点最直接的应用之一。以下是创建简单径向渐变的步骤:
- 在 Shader Graph 中创建新的 Unlit Graph
- 添加 Position 节点并设置为 Absolute World
- 添加 Vector3 属性作为渐变中心点,默认值设为 (0,0,0)
- 将 Position 和中心点属性连接到 Distance 节点的 A 和 B 端口
- 添加 Divide 节点将距离值除以外半径值进行标准化
- 添加 Saturate 节点确保结果在 0-1 范围内
- 使用标准化后的距离值作为 Lerp 节点的系数,混合内外颜色
- 连接到 Fragment 节点的 Base Color 端口
这种技术可以扩展为创建复杂的径向背景、能量护盾效果或聚焦光照效果。
案例二:实现物体边缘发光
使用 Distance 节点可以检测物体边缘并添加发光效果:
- 使用 Object 节点的 Position 输出作为基础位置
- 添加 Camera 节点的 World Position 作为观察点参考
- 计算物体表面点到摄像机方向的垂直距离
- 当距离小于阈值时应用发光颜色
- 使用指数函数控制发光的衰减曲线
- 将发光效果与基础颜色相加混合
这种方法可以创建科幻风格的轮廓光、危险物品警示效果或魔法特效。
案例三:制作交互式溶解效果
Distance 节点非常适合创建基于距离的溶解效果:
- 计算每个片段到交互点(如玩家位置)的距离
- 将距离与阈值比较,决定是否溶解
- 使用噪声纹理为溶解边缘添加细节
- 根据距离控制溶解边缘的发光强度
- 添加动画使溶解效果随时间传播
这种效果常用于角色死亡、物体破坏或魔法传送等游戏场景。
与其他节点的配合使用
与数学节点配合
Distance 节点经常与各种数学节点结合使用以实现更复杂的效果:
- 与 Divide 节点配合:标准化距离值,将其映射到特定范围
- 与 Multiply 节点配合:调整距离的影响强度或创建重复模式
- 与 Add/Subtract 节点配合:偏移距离基准点或创建距离偏移效果
- 与 Power 节点配合:创建非线性的距离衰减曲线
与高级函数节点配合
Distance 节点与一些特殊函数节点结合可以创建专业级效果:
- 与 Remap 节点配合:将距离从原始范围重新映射到新范围
- 与 Smoothstep 节点配合:创建平滑的距离过渡区域
- 与 Fraction 节点配合:基于距离创建重复图案
- 与 Noise 节点配合:为距离效果添加有机变化
在节点组中的角色
在复杂的着色器中,Distance 节点通常作为更大节点网络的一部分:
- 在距离场着色器中作为基础距离计算单元
- 在光照模型中作为衰减计算的基础
- 在后期处理效果中作为空间遮罩生成器
- 在程序化生成中作为形状描述的基本操作
生成的代码示例分析
代码结构解析
Distance 节点生成的 HLSL 代码反映了其核心功能:
HLSL
void Unity_Distance_float4(float4 A, float4 B, out float Out)
{
Out = distance(A, B);
}
这段代码展示了一个典型的四维向量距离计算函数。分析代码结构:
- 函数名为
Unity_Distance_float4,表明处理的是 float4 类型 - 接受两个 float4 参数 A 和 B
- 通过 out 参数返回计算结果
- 使用 HLSL 内置的
distance()函数进行实际计算
内置 distance 函数
HLSL 中的 distance() 函数是 Distance 节点的核心实现:
HLSL
// HLSL 内置 distance 函数的近似实现
float distance(float4 a, float4 b)
{
float4 diff = a - b;
return sqrt(dot(diff, diff));
}
这个实现展示了欧几里德距离的实际计算过程:
- 首先计算两个向量的差值
- 然后计算差值的点积(即各分量平方和)
- 最后对点积结果取平方根得到实际距离
不同维度的变体
Shader Graph 会根据输入向量维度生成不同的函数变体:
对于二维向量:
HLSL
void Unity_Distance_float2(float2 A, float2 B, out float Out)
{
Out = distance(A, B);
}
对于三维向量:
HLSL
void Unity_Distance_float3(float3 A, float3 B, out float Out)
{
Out = distance(A, B);
}
这些变体确保了无论输入数据维度如何,都能正确计算距离。
故障排除和常见问题
编译错误和解决方案
在使用 Distance 节点时可能遇到的一些常见编译错误:
维度不匹配错误:
- 问题:A 和 B 端口连接了不同维度的向量
- 解决方案:确保两个输入端口使用相同维度的向量类型
类型不兼容错误:
- 问题:尝试连接不支持的数据类型到输入端口
- 解决方案:只使用浮点数类型的向量(Float/Vector2/Vector3/Vector4)
循环依赖错误:
- 问题:节点连接形成了循环引用
- 解决方案:检查节点连接,确保数据流向是单向的
性能问题诊断
Distance 节点可能引起的性能问题及解决方法:
片段着色器过载:
- 症状:在片段着色器中大量使用 Distance 节点导致帧率下降
- 解决方案:将计算移至顶点着色器,或使用简化距离计算
精度问题:
- 症状:在远距离时出现精度误差或闪烁
- 解决方案:使用更高精度的浮点数,或重新设计距离计算范围
移动端性能问题:
- 症状:在移动设备上性能显著下降
- 解决方案:减少 Distance 节点使用频率,使用近似计算或预计算
视觉效果问题
使用 Distance 节点时可能遇到的视觉效果问题:
距离计算不准确:
- 问题:计算的距离与预期不符
- 检查点:确认使用的坐标空间是否正确,检查向量分量是否完整
渐变效果不连续:
- 问题:基于距离的渐变出现明显边界或断层
- 解决方案:检查距离标准化过程,确保使用正确的插值函数
边缘效果闪烁:
- 问题:基于距离的边缘效果在摄像机移动时闪烁
- 解决方案:为距离计算添加适当的偏导数或使用屏幕空间技术
高级应用和创意用法
有符号距离函数(SDF)应用
Distance 节点是实现有符号距离函数的基础。SDF 是描述几何形状的强大数学工具:
基本 SDF 形状:
- 球体 SDF:distance(p, center) - radius
- 盒子 SDF:计算点到立方体边界的有符号距离
- 平面 SDF:dot(p, normal) - distance
SDF 布尔运算:
- 并集:min(d1, d2)
- 交集:max(d1, d2)
- 差集:max(d1, -d2)
SDF 扭曲和变形:
- 通过噪声函数扭曲距离场
- 使用三角函数创建重复图案
- 结合时间变量创建动画 SDF
程序化动画和交互
Distance 节点可以驱动各种程序化动画效果:
波浪传播效果:
- 基于到源点的距离控制波浪相位
- 使用正弦函数创建波浪形状
- 结合时间变量创建传播动画
粒子吸引/排斥系统:
- 计算粒子到吸引点的距离
- 根据距离决定作用力强度和方向
- 创建自然的粒子运动行为
交互式变形效果:
- 基于到交互点的距离变形网格
- 使用距离控制变形强度和范围
- 创建响应玩家操作的动态环境
高级渲染技术
Distance 节点在现代渲染技术中扮演重要角色:
距离场软阴影:
- 使用距离场信息计算柔和阴影
- 通过多次距离查询估计遮挡程度
- 创建高质量的场景阴影效果
光线步进渲染:
- 使用距离场指导光线前进步骤
- 大幅提高复杂几何体的渲染效率
- 实现实时体渲染和复杂参数曲面渲染
全局光照近似:
- 基于距离估计间接光照贡献
- 创建简化的环境光遮蔽效果
- 实现性能友好的全局光照近似
【Unity Shader Graph 使用与特效实现】专栏-直达 (欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)