
Three.js 实现超酷炫的虫洞时空漩涡效果!附完整源码
特此感谢作者的贡献!!!
📸 效果预览

(虫洞时空漩涡效果,螺旋状的能量隧道带你穿越时空)
🚀 在线演示
🤔 前言
相信大家对虫洞都不陌生,在科幻作品中,虫洞是连接两个时空的隧道,让人可以瞬间穿越遥远的距离。
本篇文章将带你深入解析这个虫洞时空漩涡效果的实现原理,了解如何使用 Three.js 着色器创建令人震撼的视觉效果。
💻 核心代码解析
第一步:基础场景搭建
首先,创建 Three.js 基础环境,包括场景、相机、渲染器和轨道控制器。
javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const canvas = document.createElement('canvas');
document.body.appendChild(canvas);
const renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true,
alpha: true,
preserveDrawingBuffer: true
});
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(90, 1, 0.1, 1000);
camera.position.set(0, 0, 5);
const controls = new OrbitControls(camera, canvas);
controls.enableDamping = true;
controls.maxDistance = 20;
controls.minDistance = 1;
controls.enablePan = false;
第二步:创建虫洞着色器材质(核心)
该实现使用 RawShaderMaterial,核心是基于极坐标和螺旋噪声的隧道效果。
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
const material = new THREE.RawShaderMaterial({
glslVersion: THREE.GLSL3,
uniforms: {
center: { value: new THREE.Vector2(0.5, 0.5) },
radius: { value: 0.3 },
tunnelLength: { value: 8.0 },
rotationSpeed: { value: 1.5 },
colorIntensity: { value: 0.7 },
glowIntensity: { value: 1.0 },
pulseIntensity: { value: 0.15 },
color1: { value: new THREE.Color(0x00ffff) },
color2: { value: new THREE.Color(0xff00ff) },
color3: { value: new THREE.Color(0xffff00) },
time: { value: 0.0 },
resolution: { value: new THREE.Vector2() },
},
vertexShader: `
precision highp float;
in vec3 position;
in vec2 uv;
out vec2 V_uv;
void main() {
V_uv = uv;
gl_Position = vec4(position, 1.0);
}
`,
fragmentShader: `
precision highp float;
#define PI 3.141592653589793
uniform vec2 center;
uniform float radius;
uniform float tunnelLength;
uniform float rotationSpeed;
uniform float colorIntensity;
uniform float glowIntensity;
uniform float pulseIntensity;
uniform vec3 color1;
uniform vec3 color2;
uniform vec3 color3;
uniform float time;
uniform vec2 resolution;
in vec2 V_uv;
// 伪随机函数
float hash(vec2 p) {
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
}
// 二维噪声
float noise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
f = f * f * (3.0 - 2.0 * f);
float a = hash(i);
float b = hash(i + vec2(1.0, 0.0));
float c = hash(i + vec2(0.0, 1.0));
float d = hash(i + vec2(1.0, 1.0));
return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
}
// 分形布朗运动
float fbm(vec2 p) {
float value = 0.0;
float amplitude = 0.5;
float frequency = 1.0;
for (int i = 0; i < 5; i++) {
value += amplitude * noise(p * frequency);
amplitude *= 0.5;
frequency *= 2.0;
}
return value;
}
// 螺旋噪声
float spiralNoise(vec2 p, float twists, float time) {
float r = length(p);
float a = atan(p.y, p.x);
float angle = a + r * twists + time;
return sin(angle * 6.0) * 0.5 + 0.5;
}
void main() {
vec2 uv = V_uv;
vec2 p = uv - center;
float r = length(p);
float a = atan(p.y, p.x);
float nr = r / radius;
// 虫洞隧道效果
float tunnel = 1.0 / (nr + 0.1);
float rot = time * rotationSpeed;
// 多层螺旋
float spiral1 = spiralNoise(p * 3.0, 8.0, rot);
float spiral2 = spiralNoise(p * 5.0 + vec2(100.0), -6.0, rot * 0.7);
float spiral3 = spiralNoise(p * 2.0 + vec2(50.0), 4.0, rot * 1.3);
// 深度感
float depth = tunnelLength / (nr * tunnelLength + 1.0);
depth = clamp(depth, 0.0, tunnelLength);
// 能量条纹
float energyBands = sin(depth * 20.0 - time * 5.0 + a * 5.0) * 0.5 + 0.5;
energyBands = pow(energyBands, 3.0);
// 分形纹理
float fractal = fbm(p * 10.0 + vec2(time * 0.2));
// 组合颜色
vec3 color = color1 * spiral1 * 0.5 + color2 * energyBands * 0.5 + color3 * fractal * 0.4;
// 中心发光
float glow = exp(-nr * 6.0) * 3.0 * glowIntensity;
color += color1 * glow * 0.7 + color2 * glow * 0.3;
// 中心白点
float core = exp(-nr * 40.0) * 4.0 * glowIntensity;
color += color1 * core * 0.7 + vec3(1.0) * core * 0.3;
// 边缘暗化
float vignette = 1.0 - smoothstep(0.3, 1.8, nr);
color *= vignette;
// 能量脉冲
float pulse = sin(time * 3.0) * pulseIntensity + (1.0 - pulseIntensity * 0.5);
color *= pulse;
gl_FragColor = vec4(color, 1.0);
}
`,
});
着色器核心原理:
| 组件 | 实现方式 |
|---|---|
| 隧道效果 | 使用 1.0 / (nr + 0.1) 创建向内延伸的视觉效果 |
| 螺旋纹理 | spiralNoise() 函数生成旋转的螺旋图案 |
| 分形布朗运动 | fbm() 函数生成有机纹理细节 |
| 深度感 | 根据距离中心的远近调整亮度和颜色 |
| 能量条纹 | 使用正弦函数生成动态条纹 |
| 中心发光 | 使用指数函数创建渐变光晕 |
第三步:创建全屏平面
javascript
1
2
3
const geometry = new THREE.PlaneGeometry(2, 2);
const quad = new THREE.Mesh(geometry, material);
scene.add(quad);
第四步:动画循环与GUI控制
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
29
30
31
32
33
34
35
36
37
38
39
40
41
const clock = new THREE.Clock();
const data = {
radius: 0.3,
tunnelLength: 8.0,
rotationSpeed: 1.5,
colorIntensity: 0.7,
color1: '#00ffff',
color2: '#ff00ff',
color3: '#ffff00',
glowIntensity: 1.0,
pulseIntensity: 0.15,
};
import GUI from 'lil-gui';
const gui = new GUI();
gui.add(data, 'radius', 0.1, 0.5, 0.01).name('半径');
gui.add(data, 'tunnelLength', 1.0, 20.0, 0.1).name('隧道长度');
gui.add(data, 'rotationSpeed', 0.0, 5.0, 0.1).name('旋转速度');
gui.add(data, 'colorIntensity', 0.0, 1.0, 0.01).name('颜色强度');
gui.add(data, 'glowIntensity', 0.0, 2.0, 0.01).name('发光强度');
gui.add(data, 'pulseIntensity', 0.0, 0.5, 0.01).name('脉冲强度');
gui.addColor(data, 'color1').name('颜色1');
gui.addColor(data, 'color2').name('颜色2');
gui.addColor(data, 'color3').name('颜色3');
const tick = (delta, elapsed) => {
controls.update(delta);
material.uniforms.time.value = elapsed;
material.uniforms.radius.value = data.radius;
material.uniforms.tunnelLength.value = data.tunnelLength;
material.uniforms.rotationSpeed.value = data.rotationSpeed;
material.uniforms.color1.value.set(data.color1);
material.uniforms.color2.value.set(data.color2);
material.uniforms.color3.value.set(data.color3);
};
const ani = () => {
const elapsed = clock.getElapsedTime();
const delta = clock.getDelta();
tick(delta, elapsed);
renderer.render(scene, camera);
requestAnimationFrame(ani);
};
ani();
� 关键技术点
| 技术点 | 说明 | 实现位置 |
|---|---|---|
| RawShaderMaterial | 自定义GLSL着色器,支持GLSL 3.0 | 材质定义 |
| 伪随机函数 | hash() 生成随机值用于噪声 | 着色器函数 |
| 二维噪声 | noise() 生成基础噪声 | 着色器函数 |
| 分形布朗运动 | fbm() 生成复杂有机纹理 | 着色器函数 |
| 螺旋噪声 | spiralNoise() 生成旋转漩涡纹理 | 着色器函数 |
| 极坐标转换 | 使用 atan() 转换为极坐标 | main() 函数 |
🔧 常见问题
问题1:隧道效果不明显
原因:radius 或 tunnelLength 参数设置不当
解决:
- 增大
radius值使隧道更宽 - 增大
tunnelLength值增强深度感
问题2:颜色不够鲜艳
原因:colorIntensity 参数值过低
解决:
- 增大
colorIntensity参数(范围 0-1) - 调整三种颜色选择更鲜艳的配色
问题3:性能较差
原因:分形噪声计算复杂度较高
优化方向:
- 减少 fbm() 循环次数(当前5次)
- 降低噪声采样频率
📌 总结
本文介绍的虫洞时空漩涡效果实现了:
- 隧道视觉效果:使用极坐标和倒数函数创建向内延伸的隧道感
- 螺旋纹理:多层螺旋噪声叠加产生复杂的漩涡图案
- 分形布朗运动:生成自然有机的纹理细节
- 动态效果:旋转、脉冲、闪烁等多种动画效果
- 中心发光:模拟虫洞入口的明亮核心
💡 扩展方向
- 添加粒子系统模拟穿越虫洞时的粒子效果
- 实现视角移动效果,让用户感觉在穿越隧道
- 添加声音效果增强沉浸感
- 实现不同阶段的虫洞形态变化
本文档由 ThreeLab 编辑整理,基于开源实现整理,如需转载,请注明出处。







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