Three.js 实战:打造一个科幻感十足的动态隧道效果
本文将带你实现一个 动态隧道穿梭效果 ——画面中一个由立方体组成的隧道不断向前延伸,相机与光源运动叠加,营造出仿佛进入科幻电影的视觉冲击感。
一、效果概述
最终效果包括几个关键要素:
-
无尽延伸的隧道:由立方体环组成,每当相机“穿过”一环,它会被重新放置到最远端,从而形成无限循环。
-
推进的相机:不断沿 Z 轴前进,给人进入隧道的沉浸感。
-
旋转的光源:点光源围绕中心轨迹运动,使隧道内部色彩和明暗不断变化。
-
整体旋转的相机:微微绕 Z 轴旋转,进一步增强动态氛围。

二、搭建基础场景
1. 初始化渲染器与场景
var ww = window.innerWidth,
wh = window.innerHeight;
renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('scene') });
renderer.setSize(ww, wh);
scene = new THREE.Scene();-
WebGLRenderer将三维画面渲染到指定<canvas>元素上。 -
Scene是承载所有物体、光源、相机的舞台。
2. 配置相机
camera = new THREE.PerspectiveCamera(50, ww/wh, 1, 10000);
scene.add(camera);-
使用 透视相机 (
PerspectiveCamera) 来模拟真实视觉。 -
参数含义:
-
50:视野角度(FOV),类似人眼视角。 -
ww/wh:画布宽高比,避免拉伸。 -
1, 10000:近裁剪面与远裁剪面,控制可见深度范围。
-
3. 添加点光源
light = new THREE.PointLight(0xffffff, 1, 1300);
light.position.set(0, 0, -750);
scene.add(light);-
点光源从一个点向四周发射光线。
-
0xffffff白色光,1强度,1300衰减范围。 -
光源位置设置在相机前方,保证初始画面有光照效果。
三、构建隧道
隧道并不是一整条曲面,而是由多个 环形排列的立方体 堆叠而成。
1. 定义几何与颜色
var colors = [
new THREE.Color(0x2cc6e5),
new THREE.Color(0x91f4f4),
new THREE.Color(0xe6a09e),
new THREE.Color(0x1071ac)
];
var geometry = new THREE.BoxGeometry(50, 50, 150);
var translate = new THREE.Matrix4().makeTranslation(150, 0, 0);-
每个隧道模块是一个长方体 (
50×50×150)。 -
通过
Matrix4.makeTranslation把立方体偏移到环的半径上。 -
颜色数组用来循环赋值,增加视觉变化。
2. 生成环
for(var i=0;i<14;i++){
var circle = new THREE.Object3D();
for(var j=0;j<13;j++){
var material = new THREE.MeshLambertMaterial({color: colors[i%4]});
var cube = new THREE.Mesh(geometry, material);
var rotation = new THREE.Matrix4().makeRotationZ(Math.PI*2/13*j);
cube.applyMatrix(new THREE.Matrix4().multiplyMatrices(rotation, translate));
circle.add(cube);
}
circle.position.z = -i*180;
elements.add(circle);
}-
外层循环:生成 14 个环,沿 Z 轴堆叠。
-
内层循环:每个环由 13 个立方体组成,通过 旋转矩阵 均匀分布在圆周上。
-
每个环往后退
180个单位,形成连续隧道。
四、动画与无限延伸
动画通过 requestAnimationFrame 循环执行,分三部分:
1. 无限延伸逻辑
for(var i=0;i<14;i++){
var circle = elements.children[i];
if(camera.position.z <= circle.position.z){
farest -= 180;
circle.position.z = farest;
}
}-
当相机超过某个环时,将其移动到最远端(
farest变量记录隧道尽头)。 -
这样不会真正增加几何体数量,但能营造 无尽延伸 的效果。
2. 相机与光源运动
camera.rotation.z += 0.005;
camera.position.z -= 7;
light.position.z -= 7;
light.position.y = Math.sin(counter/50) * 75;
light.position.x = Math.cos(counter/50) * 75;-
相机沿 Z 轴前进,同时绕自身 Z 轴缓慢旋转,增加画面旋转感。
-
光源随着时间在 XY 平面做圆周运动,让光影不断变化。
3. 渲染循环
counter++;
renderer.render(scene, camera);-
renderer.render每一帧都重新绘制场景。 -
counter累加器驱动光源位置变化。
源码
https://codepen.io/Mamboleoo/pen/emazBa (opens in a new tab)