Three.js 雷达波纹效果

631 阅读4分钟

Three.js 雷达波纹效果

效果概述

这个雷达波纹效果具有以下特点:

  • 从中心点向外扩散的同心圆波纹(类似投石入水的涟漪)
  • 波纹具有渐变的透明度效果
  • 支持多个波纹同时存在
  • 可通过 GUI 实时调整参数

与传统雷达扫描的区别:

  • 传统雷达扫描:一条射线围绕中心旋转,形成扇形扫描区域
  • 这个效果:圆形波纹从中心向外扩散,模拟声波或电磁波的传播

RadarPing 函数详解

让我们先看看核心的 RadarPing 函数:

vec3 RadarPing(in vec2 uv, in vec2 center, in float innerTail,
               in float frontierBorder, in float timeResetSeconds,
               in float radarPingSpeed, float t)
{
    vec2 diff = center-uv;
    float r = length(diff);
    float time = mod(t, timeResetSeconds) * radarPingSpeed;
    
    float ring = smoothstep(time - innerTail, time, r) * (1.0 - smoothstep(time, time + frontierBorder, r));
    
    return vec3(ring);
}

参数解析

  • uv: 当前像素的纹理坐标
  • center: 雷达中心点位置
  • innerTail: 波纹内侧渐变长度
  • frontierBorder: 波纹外侧边界厚度
  • timeResetSeconds: 波纹循环周期
  • radarPingSpeed: 波纹扩散速度
  • t: 当前时间

核心算法分析

1. 距离计算
vec2 diff = center-uv;
float r = length(diff);

计算当前像素到雷达中心的距离。这是整个效果的基础,因为雷达波纹是以同心圆的形式扩散的。

2. 时间循环处理
float time = mod(t, timeResetSeconds) * radarPingSpeed;

这里使用 mod 函数的原因:

  • 循环效果mod(t, timeResetSeconds) 将时间限制在 0 到 timeResetSeconds 之间,创造无限循环的效果
  • 速度控制:乘以 radarPingSpeed 可以控制波纹扩散的快慢
  • 重置机制:当时间达到周期上限时自动归零,波纹从中心重新开始
3. 波纹形状生成
float ring = smoothstep(time - innerTail, time, r) * (1.0 - smoothstep(time, time + frontierBorder, r));

这是最关键的部分,让我们分解理解:

第一部分:smoothstep(time - innerTail, time, r)

  • 创建波纹的内侧渐变
  • r < time - innerTail 时,返回 0(完全透明)
  • r > time 时,返回 1(完全不透明)
  • time - innerTailtime 之间平滑过渡

第二部分:(1.0 - smoothstep(time, time + frontierBorder, r))

  • 创建波纹的外侧渐变
  • r < time 时,返回 1(完全不透明)
  • r > time + frontierBorder 时,返回 0(完全透明)
  • timetime + frontierBorder 之间平滑过渡

两部分相乘的效果:

  • 只有在特定距离范围内的像素才会显示
  • 形成一个从内到外渐变的环形区域
  • 随着时间推移,这个环形区域不断向外扩散

为什么使用 smoothstep?

smoothstep 函数是 GLSL 中的内置函数,它提供平滑的 S 型插值曲线。相比于线性插值,它有以下优势:

  1. 视觉效果更自然:S 型曲线在边界处变化缓慢,中间变化较快,符合人眼对渐变的感知
  2. 避免硬边缘:直接使用 step 函数会产生锐利的边缘,而 smoothstep 创造平滑过渡
  3. 性能优化:GPU 对 smoothstep 有专门优化,比自定义的平滑函数更高效

多环效果的实现

在主函数中,通过循环创建多个波纹:

for(int i = 0; i < MAX_RINGS; i++){
    if (i >= u_ringCount) break;
    
    float timeOffset = float(i) * (resetTimeSec / float(u_ringCount)); 
    
    vec3 ping = RadarPing(uv, pingCenter, u_gradientRange, 0.00025, resetTimeSec, u_speed, iTime + timeOffset);
    totalAlpha += ping.r;
}

关键在于 timeOffset

  • 为每个波纹添加不同的时间偏移
  • 确保波纹之间有规律的间隔
  • 创造连续扩散的视觉效果

实际应用技巧

1. 参数调优建议

  • innerTail (渐变范围):0.05-0.15 之间效果较好
  • frontierBorder:设置很小的值(如 0.00025)创造锐利的外边缘
  • 速度:根据场景需求调整,游戏中通常 0.1-0.5 合适

2. 性能优化

  • 限制最大环数(MAX_RINGS)避免过度绘制
  • 使用 discard 丢弃完全透明的片元
  • 将 alpha 值限制在合理范围内避免过曝

3. 视觉增强

  • 可以添加噪声函数增加真实感
  • 结合粒子系统模拟雷达探测到的目标
  • 添加颜色变化模拟不同强度的信号

总结

RadarPing 函数巧妙地结合了距离计算、时间循环和平滑插值,创造出逼真的雷达扫描效果。mod 函数确保了动画的循环播放,而 smoothstep 函数则提供了自然的渐变过渡。理解这些基础概念后,你就可以创造出各种变化的波纹效果,应用到游戏、数据可视化或艺术创作中。

这种基于数学函数的程序化效果不仅性能优秀,而且高度可控,是现代图形编程中的重要技术。