Three.js 雷达扫描着色器 | 三维可视化 / AI 提示词

📋 AI 提示词

prompt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
使用 Three.js 实现**雷达扫描着色器**,具体要求:

【核心特效】
- 360度旋转扫描线
- 扫描尾迹渐变消失效果
- 单一颜色配置
- 简洁的雷达显示效果

【场景与光照】
- 深灰色背景 (#1f1f1f)
- 从上方俯视角度 (相机位置 y=800, z=1000)
- OrbitControls 轨道控制器
- 自定义材质实现扫描动画

【交互与控制】
- OrbitControls 鼠标拖拽旋转
- 鼠标滚轮缩放
- 自动播放扫描动画

【技术要求】
- Three.js 版本 (ES Module)
- MeshBasicMaterial + onBeforeCompile 实现自定义着色器
- 使用 `atan()` 函数计算角度
- 实时更新的 uniform 时间变量

🖼️ 效果预览

Three.js 雷达扫描着色器

🎮 案例演示

立即体验


📖 效果拆解

层次 视觉效果 技术实现
基础 圆形雷达盘 CircleGeometry (半径240, 1000分段) + 旋转矩阵
核心特效 旋转扫描线 片元着色器基于角度差的尾迹效果
增强细节 尾迹渐变透明 透明度随角度差增大而线性衰减
交互控制 自动扫描动画 时间 uniform 控制扫描角度

🔧 核心技术点

1. 扫描线角度计算

为什么需要这个技术:实现雷达扫描线绕中心旋转的效果,通过计算每个像素与扫描线的角度差来实现。

glsl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
float calcAngle(vec3 oFrag) {
  float fragAngle;
  const vec3 ox = vec3(1, 0, 0);

  float dianji = oFrag.x * ox.x + oFrag.z * ox.z;
  float oFrag_length = length(oFrag);
  float ox_length = length(ox);
  float yuxian = dianji / (oFrag_length * ox_length);

  fragAngle = acos(yuxian);
  fragAngle = degrees(fragAngle);

  if(oFrag.z > 0.0) {
    fragAngle = -fragAngle + 360.0;
  }

  float scanAngle = uTime * uSpeed - floor(uTime * uSpeed / 360.0) * 360.0;
  float angle = scanAngle - fragAngle;
  if(angle < 0.0) {
    angle = angle + 360.0;
  }

  return angle;
}

2. 尾迹渐变透明度

为什么需要这个技术:让扫描线后面产生逐渐消失的尾迹效果,模拟真实雷达的磷光余晖。

glsl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void main() {
  #include <opaque_fragment>

  if(length(vPosition) == 0.0 || length(vPosition) > uRadius - 2.0) {
    gl_FragColor = vec4(outgoingLight, diffuseColor.a);
  } else {
    float angle = calcAngle(vPosition);

    if(angle < uFollowWidth) {
      float opacity = 1.0 - angle / uFollowWidth;
      gl_FragColor = vec4(outgoingLight, diffuseColor.a * opacity);
    } else {
      gl_FragColor = vec4(outgoingLight, 0.0);
    }
  }
}

3. 圆形几何体旋转放置

为什么需要这个技术:将平面圆形变为水平放置的雷达盘面,默认 CircleGeometry 是垂直于 Y 轴的。

javascript
1
2
3
const circleGeometry = new THREE.CircleGeometry(radarData.radius, 1000);
const rotateMatrix = new THREE.Matrix4().makeRotationX((-Math.PI / 180) * 90);
circleGeometry.applyMatrix4(rotateMatrix);

4. onBeforeCompile 自定义着色器

为什么需要这个技术:在现有材质的基础上注入自定义的 uniform 和着色器代码,无需完全重写。

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
material.onBeforeCompile = (shader) => {
  Object.assign(shader.uniforms, {
    uSpeed: { value: radarData.speed },
    uRadius: { value: radarData.radius },
    uTime: { value: 0 },
    uFollowWidth: { value: radarData.followWidth }
  });

  requestAnimationFrame(function update(time) {
    shader.uniforms.uTime.value = time / 1000;
    requestAnimationFrame(update);
  });

  shader.vertexShader = shader.vertexShader.replace('void main() {',
    `varying vec3 vPosition;
     void main() {
       vPosition = position;
  `);

  shader.fragmentShader = shader.fragmentShader.replace('void main() {',
    `uniform float uRadius;
     uniform float uTime;
     uniform float uSpeed;
     uniform float uFollowWidth;
     varying vec3 vPosition;

     float calcAngle(vec3 oFrag) {
       // ... 计算逻辑
     }

     void main() {
  `);
};

5. 雷达参数配置

为什么需要这个技术:集中管理雷达的各项参数,便于调试和修改。

javascript
1
2
3
4
5
6
7
8
const radarData = {
  position: { x: 0, y: 20, z: 0 },
  radius: 240,
  color: '#f005f0',
  opacity: 0.5,
  speed: 300,
  followWidth: 220
};

💡 调试与优化

问题类型 表现形式 解决方案
扫描线跳跃 扫描到360度时突然重置 确保 scanAngle 取模计算正确
中心盲区 原点附近显示异常 添加 length(vPosition) == 0.0 判断
尾迹过窄 扫描线尾迹不明显 增加 uFollowWidth 参数值
性能问题 分段数过高 减少 CircleGeometry 分段数 (1000→500)

🚀 扩展思路

  1. 多目标显示:添加可移动的目标点标记
  2. 历史轨迹:记录并显示目标的历史移动轨迹
  3. 多雷达叠加:创建多个扫描线叠加效果
  4. 颜色渐变:扫描线颜色随距离或角度变化
  5. 3D球形雷达:扩展到3D空间的球形扫描

📚 相关学习资源


©️ 版权声明

*本文档由 ThreeLab 编辑整理,专注 Three.js 着色器、Web 3D、GIS 可视化技术分享。如需转载,请注明出处。