雷达扫描

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 时间变量
🖼️ 效果预览
🎮 案例演示
📖 效果拆解
| 层次 | 视觉效果 | 技术实现 |
|---|---|---|
| 基础 | 圆形雷达盘 | 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) |
🚀 扩展思路
- 多目标显示:添加可移动的目标点标记
- 历史轨迹:记录并显示目标的历史移动轨迹
- 多雷达叠加:创建多个扫描线叠加效果
- 颜色渐变:扫描线颜色随距离或角度变化
- 3D球形雷达:扩展到3D空间的球形扫描
📚 相关学习资源
©️ 版权声明
*本文档由 ThreeLab 编辑整理,专注 Three.js 着色器、Web 3D、GIS 可视化技术分享。如需转载,请注明出处。







京公网安备 11010502038735号
💬 评论区
评论功能即将上线,敬请期待!