Threejs案例
Suidao

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)