Three.js 曲线应用详解

55 阅读4分钟

概述

本文将详细介绍如何使用 Three.js 中的曲线功能。我们将学习如何创建 CatmullRomCurve3 样条曲线,并利用曲线来控制物体的运动轨迹,以及如何将曲线可视化地显示在场景中。

screenshot_2026-01-28_17-49-20.gif

准备工作

首先,我们需要引入必要的 Three.js 库:

import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

场景初始化

首先,我们需要创建一个基本的 Three.js 场景:

let camera, scene, renderer;
const clock = new THREE.Clock();
const textureLoader = new THREE.TextureLoader();

let moon;
let earth;
let curve;
const raycaster = new THREE.Raycaster();

function init() {
  const EARTH_RADIUS = 1;
  const MOON_RADIUS = 0.27;

  // 创建透视相机
  camera = new THREE.PerspectiveCamera(
    45,
    window.innerWidth / window.innerHeight,
    1,
    200
  );
  camera.position.set(0, 5, -10);

  // 初始化场景
  scene = new THREE.Scene();
}

光照设置

添加适当的光照使场景更真实:

// 方向光
const dirLight = new THREE.DirectionalLight(0xffffff);
dirLight.position.set(0, 0, -1);
scene.add(dirLight);

// 环境光
const light = new THREE.AmbientLight(0xffffff, 0.5); // soft white light
scene.add(light);

创建地球和月球模型

使用纹理贴图创建地球和月球模型:

// 创建地球
const earthGeometry = new THREE.SphereGeometry(EARTH_RADIUS, 16, 16);
const earthMaterial = new THREE.MeshPhongMaterial({
  specular: 0x333333,
  shininess: 5,
  map: textureLoader.load("textures/planets/earth_atmos_2048.jpg"),
  specularMap: textureLoader.load("textures/planets/earth_specular_2048.jpg"),
  normalMap: textureLoader.load("textures/planets/earth_normal_2048.jpg"),
  normalScale: new THREE.Vector2(0.85, 0.85),
});

earth = new THREE.Mesh(earthGeometry, earthMaterial);
scene.add(earth);

// 创建月球
const moonGeometry = new THREE.SphereGeometry(MOON_RADIUS, 16, 16);
const moonMaterial = new THREE.MeshPhongMaterial({
  shininess: 5,
  map: textureLoader.load("textures/planets/moon_1024.jpg"),
});
moon = new THREE.Mesh(moonGeometry, moonMaterial);
scene.add(moon);

创建 CatmullRomCurve3 曲线

这是关键部分,我们使用一系列点来创建平滑的 CatmullRomCurve3 样条曲线:

// 根据这一系列的点创建闭合曲线
curve = new THREE.CatmullRomCurve3(
  [
    new THREE.Vector3(-10, 0, 10),  // 起始点
    new THREE.Vector3(-5, 5, 5),    // 控制点1
    new THREE.Vector3(0, 0, 5),     // 控制点2
    new THREE.Vector3(5, -5, 5),    // 控制点3
    new THREE.Vector3(10, 0, 10),   // 结束点
  ],
  true  // true 表示创建闭合曲线,false 表示开放曲线
);

CatmullRomCurve3 曲线是一种样条曲线,它会经过所有的控制点,形成平滑的路径。参数 true 表示创建闭合曲线,即曲线的终点会连接到起点。

可视化曲线

将曲线可视化地显示在场景中:

// 在曲线里,getPoints获取501个点(500个分段+1)
const points = curve.getPoints(500);
console.log(points);
const geometry = new THREE.BufferGeometry().setFromPoints(points);

const material = new THREE.LineBasicMaterial({ color: 0xff0000 });

// 创建线条对象并添加到场景
const curveObject = new THREE.Line(geometry, material);
scene.add(curveObject);

getPoints(divisions) 方法会沿着曲线均匀地采样指定数量的点。在这里,我们使用 500 个分段,因此会得到 501 个点(包括起始点和结束点)。这样可以确保曲线看起来平滑流畅。

渲染器和控制器设置

设置渲染器和控制器:

renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

const controls = new OrbitControls(camera, renderer.domElement);
controls.minDistance = 5;
controls.maxDistance = 100;

动画循环与曲线运动

在动画循环中,我们让月球沿着曲线运动,同时相机也跟随曲线:

function animate() {
  requestAnimationFrame(animate);

  const elapsed = clock.getElapsedTime();
  // 计算归一化的时间参数(0到1之间)
  const time = elapsed/10 % 1;
  
  // 根据时间参数获取曲线上对应的点
  const point = curve.getPoint(time);
  
  // 将月球位置设置为曲线上当前点的位置
  moon.position.copy(point);
  
  // 相机也跟随曲线运动
  camera.position.copy(point);
  
  // 相机始终看向地球位置
  camera.lookAt(earth.position)
  
  // 渲染场景
  renderer.render(scene, camera);
}

getPoint(t) 方法用于获取曲线上特定参数位置的点,其中 t 是一个介于 0 和 1 之间的值。当 t=0 时,返回曲线的起始点;当 t=1 时,返回曲线的结束点(在闭合曲线的情况下,这与起始点相同)。

曲线类型详解

Three.js 提供了多种曲线类型:

  1. CatmullRomCurve3: 经过所有控制点的三次样条曲线
  2. CubicBezierCurve3: 三次贝塞尔曲线
  3. QuadraticBezierCurve3: 二次贝塞尔曲线
  4. LineCurve3: 直线段

CatmullRomCurve3 曲线参数

CatmullRomCurve3 曲线构造函数接受以下参数:

new THREE.CatmullRomCurve3(points, closed, curveType, tension)
  • points: Vector3 类型的数组,定义曲线经过的点
  • closed: Boolean 类型,是否闭合曲线
  • curveType: String 类型,默认为 "centripetal",还有 "chordal" 和 "catmullrom"
  • tension: Number 类型,张力参数,默认为 0.5,值越大曲线越紧绷

应用场景

曲线在 Three.js 中有多种应用场景:

  1. 路径动画: 让物体沿着预设路径运动
  2. 摄像机动画: 让摄像机沿着路径移动,创建电影般的镜头效果
  3. 地形生成: 使用曲线生成平滑的地形轮廓
  4. 粒子系统: 控制粒子的运动轨迹
  5. UI 动画: 创建复杂的 UI 动画效果

总结

通过这个项目,我们学习了如何使用 Three.js 的曲线功能:

  1. 如何创建 CatmullRomCurve3 样条曲线
  2. 如何将曲线可视化显示在场景中
  3. 如何让物体沿着曲线运动
  4. 如何控制动画的时间参数
  5. 曲线在 3D 应用中的多种用途

曲线是 Three.js 中一个非常强大的功能,它可以让我们创建流畅的动画和复杂的路径效果,为我们的 3D 应用增添更多的动态美感。