在项目中经常会有让物体沿着预定路径轨迹运行的需求,例如一辆车沿着道路行驶、生产线上的产品沿着产线移动等等。实现方式:
- 绘制路径:使用Threejs提供的CatmullRomCurve3类,该类使用Catmull-Rom算法, 通过一系列的点创建一条平滑的三维样条曲线。当然,也可以在建模工具中绘制好曲线,导出供Threejs使用。
- 移动:在每一帧中按照一定步长获取路径上点,更新模型位置。
- 搭建场景
首先我们用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)
}