学完按照规律生成各种几何体,我们可以生成各种形状的管道:

这节我们来做个实战:隧道穿梭
创建项目:
mkdir tube-travel
cd tube-travel
npm init -y

进入项目,安装下 ts 类型:
npm install --save-dev @types/three
创建 index.html 和 index.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body {
margin: 0;
}
</style>
</head>
<body>
<script type="importmap">
{
"imports": {
"three": "https://esm.sh/three@0.174.0/build/three.module.js",
"three/addons/": "https://esm.sh/three@0.174.0/examples/jsm/"
}
}
</script>
<script type="module" src="./index.js"></script>
</body>
</html>
index.js
import * as THREE from 'three';
import {
OrbitControls
} from 'three/addons/controls/OrbitControls.js';
import mesh from './mesh.js';
const scene = new THREE.Scene();
scene.add(mesh);
const pointLight = new THREE.PointLight(0xffffff, 200);
pointLight.position.set(80, 80, 80);
scene.add(pointLight);
const width = window.innerWidth;
const height = window.innerHeight;
const camera = new THREE.PerspectiveCamera(60, width / height, 1, 10000);
camera.position.set(200, 200, 200);
camera.lookAt(0, 0, 0);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height)
function render() {
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
document.body.append(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
创建 Scene、Light、PerspectiveCamera、Renderer
然后创建 mesh.js
import * as THREE from 'three';
const path = new THREE.CatmullRomCurve3([
new THREE.Vector3(-100, 20, 90),
new THREE.Vector3(-40, 80, 100),
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(60, -60, 0),
new THREE.Vector3(100, -40, 80),
new THREE.Vector3(150, 60, 60)
]);
const geometry = new THREE.TubeGeometry(path, 100, 5, 30);
const material = new THREE.MeshBasicMaterial({
color: new THREE.Color('orange'),
side: THREE.DoubleSide
});
const mesh = new THREE.Mesh(geometry, material);
export default mesh;
用三维样条曲线 CatmullRomCurve3 创建穿过 6 个点的一条曲线。
然后用 TubeGeometry 创建管道几何体。
设置管道分段数 100,圆分段数 30,半径 5
跑一下:
npx live-server


这样,一条弯曲的管道就完成了。
我们找个图片作为纹理贴图:

加载一下:

const loader = new THREE.TextureLoader();
const texture = loader.load('./stone.png')
texture.wrapS = THREE.RepeatWrapping;
texture.repeat.x = 20;
const material = new THREE.MeshBasicMaterial({
map: texture,
side: THREE.DoubleSide
});
x 方向重复 20 次
看下效果:

颜色不大对,改下 colorSpace:

texture.colorSpace = THREE.SRGBColorSpace;

现在颜色对了,但是凹凸不平的颗粒感还不大够。
再加一下受环境光影响的纹理贴图:


是不是好多了?
然后我们接下来就要进入管道内部了。
怎么做呢?
其实你用 OrbitControls 看下就知道了。

OrbitControls 可以控制相机 camera 的位置和 lookAt,让相机进入管道内部,然后不断向前即可。
所以说,我们只要改变 camera 位置和 lookAt 就能做到穿梭管道的效果。
但管道是弯曲的,如何确定 camera 的位置和方向呢?
其实也简单,从曲线上取一堆密一点的点,然后 camera.position 设置当前点,lookAt 看向下一个点就可以了。

我们取 1000 个均匀的点。
export const tubePoints = path.getSpacedPoints(1000);
然后在渲染循环里不断更新 camera 的 position 和 lookAt


import mesh, { tubePoints } from './mesh.js';
let i = 0;
function render() {
if(i< tubePoints.length - 1) {
camera.position.copy(tubePoints[i]);
camera.lookAt(tubePoints[i + 1]);
i += 1;
} else {
i = 0;
}
renderer.render(scene, camera);
requestAnimationFrame(render);
}
copy 就是复制传入的点的坐标。
看下效果:

现在就是穿梭隧道的感觉了,并且还会转弯。
我们还可以把它改成键盘控制:

document.addEventListener('keydown', (e) => {
if(e.code === 'ArrowDown') {
i += 10;
}
})
按向下的键才会动。

案例代码上传了小册仓库。
总结
这节我们做了一个穿梭隧道的实战,核心目的还是练习曲线 Curve、生成几何体(比如 TubeGeometry)的 API。
我们用三维样条曲线画了穿过 n 个点的三维曲线,然后用 TubeGeometry 生成管道。
给它设置了纹理贴图,调整颜色空间,设置 map、aoMap 之后,真实感就很强了。
然后通过改变 camera 的 position 和 lookAt 实现了镜头穿梭隧道的感觉。
相机的位置是通过 curve.getSpacedPoints 取的一堆均匀的点。
经过这个实战之后,相信你对曲线、生成几何体、相机运动等就有更深的理解了。