Threejs曲线与轨迹运动

2024-02-21 17:24 月霖 821

在项目中经常会有让物体沿着预定路径轨迹运行的需求,例如一辆车沿着道路行驶、生产线上的产品沿着产线移动等等。实现方式:

  • 绘制路径:使用Threejs提供的CatmullRomCurve3类,该类使用Catmull-Rom算法, 通过一系列的点创建一条平滑的三维样条曲线。当然,也可以在建模工具中绘制好曲线,导出供Threejs使用。
  • 移动:在每一帧中按照一定步长获取路径上点,更新模型位置。
  1. 搭建场景

首先我们用threejs搭建一个基本的场景环境。

import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'

// 创建场景
const scene = new THREE.Scene()

// 创建相机
const camera = new THREE.PerspectiveCamera(
  45,
  window.innerWidth / window.innerHeight,
  0.1,
  5000
)
camera.position.set(0, 0, -10)
camera.lookAt(0, 0, 0)

const dirLight = new THREE.DirectionalLight(0xffffff, 2.0);
dirLight.position.set(0, 0, 1);
scene.add(dirLight);
const light = new THREE.AmbientLight(0xffffff, 0.5); // soft white light
scene.add(light);

const textureLoader = new THREE.TextureLoader()

// 创建物体
const sphereGeometry = new THREE.SphereGeometry(1, 16, 16)
const sphereMaterial = new THREE.MeshPhongMaterial({
  shininess: 5,
  map: textureLoader.load("texture/planets/moon_1024.jpg"),
})
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial)
scene.add(sphere)


// 创建WebGL渲染器
const renderer = new THREE.WebGLRenderer({
  antialias: true
})
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.toneMapping = THREE.CineonToneMapping
renderer.toneMappingExposure = 1.0
document.body.appendChild(renderer.domElement)

// 坐标辅助器
const axesHelper = new THREE.AxesHelper(5)
scene.add(axesHelper)

// 创建轨道控制器
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true
controls.dampingFactor = 0.1

// 渲染场景
function render() {
  controls.update()
  renderer.render(scene, camera)

  requestAnimationFrame(render)
}

render()

window.addEventListener('resize', () => {
  // 重置渲染器宽高比
  renderer.setSize(window.innerWidth, window.innerHeight)
  // 重置相机宽高比
  camera.aspect = window.innerWidth / window.innerHeight
  // 更新相机投影矩阵
  camera.updateProjectionMatrix()
})

2.创建曲线

下面的代码中,我们调用 CatmullRomCurve3 的构造函数创建曲线。接着调用 getPoints 方法获取该曲线上一定数量的点,并使用这些点来创建几何体,这里只是为了在场景中将这条曲线显示出来作为辅助查看。

const curve = new THREE.CatmullRomCurve3([
  new THREE.Vector3( -10, 0, 5 ),
  new THREE.Vector3( -5, 5, -5 ),
  new THREE.Vector3( 0, 0, -5 ),
  new THREE.Vector3( 5, -5, -5 ),
  new THREE.Vector3( 10, 0, 5 )
], true)

// 这里将创建的曲线物体添加到了场景中,仅用作辅助查看。
const points = curve.getPoints(500)
const geometry = new THREE.BufferGeometry().setFromPoints(points)
const material = new THREE.LineBasicMaterial({ color: 0xff0000 })
const curveObject = new THREE.Line(geometry, material);
scene.add(curveObject) 

3.让圆球沿着曲线运动

在渲染函数中,每一帧从曲线上获取一个点, getPoint 方法接收一个参数,该参数表示曲线上的位置,取值在[0,1]的范围内(即整条曲线长度为1),该方法返回该位置对应的点的坐标,我们将物体移动到该坐标上即可。

const clock = new THREE.Clock()
const speed = 0.15
function render() {
  const time = clock.getElapsedTime() // 获取每一帧经过的时间
  const point = curve.getPoint(time * speed % 1) // 从曲线上获取点的坐标
  sphere.position.copy(point)

  controls.update()

  renderer.render(scene, camera)

  requestAnimationFrame(render)
}