Skip to content

曲线绘制

🕒 Published at:

Curve 绘制曲线

本片实现一个CatmullRomCurve3曲线,然后让一个小球在这条线上移动

使用Catmull-Rom算法, 从一系列的点创建一条平滑的三维样条曲线。

思路

首先创造一系列点,然后将他们连接成线

js
const curve = new THREE.CatmullRomCurve3(
[
    new THREE.Vector3(-10, 0, 10),
    new THREE.Vector3(-5, 5, 5),
    new THREE.Vector3(0, 10, 0),
    new THREE.Vector3(15, -15, 15),
    new THREE.Vector3(20, 0, -20)
],
true // 表示曲线是否闭合
);
const points = curve.getPoints(50);
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const material = new THREE.LineBasicMaterial({ color: 0xff0000 });
const line = new THREE.Line(geometry, material);

scene.add(line);

然后创建一个小球,关键如何在这个线上移动呢,通过getPoint方法,传入一个0-1的数值,返回一个点,然后将这个点赋值给小球的position属性,就可以实现小球在这条线上移动了

js
const t = (Date.now() * 0.001) / 10; // 控制小球移动的速度
const position = curve.getPointAt(t % 1); // t % 1 保证 t 在 0-1 之间循环
sphere.position.copy(position);

效果

代码实现

html
<template>
  <div id="canvas"></div>
</template>
<script setup>
import { onMounted } from 'vue';
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import Stats from 'three/addons/libs/stats.module.js';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

onMounted(() => {
  let scene = new THREE.Scene();
  scene.add(new THREE.AmbientLight(0xffffff));
  const light = new THREE.DirectionalLight(0xffffff, 1.5);
  light.position.set(0, 0, 1);
  scene.add(light);

  const curve = new THREE.CatmullRomCurve3(
    [
      new THREE.Vector3(-10, 0, 10),
      new THREE.Vector3(-5, 5, 5),
      new THREE.Vector3(0, 10, 0),
      new THREE.Vector3(15, -15, 15),
      new THREE.Vector3(20, 0, -20)
    ],
    true // 表示曲线是否闭合
  );
  const points = curve.getPoints(50);
  const geometry = new THREE.BufferGeometry().setFromPoints(points);
  const material = new THREE.LineBasicMaterial({ color: 0xff0000 });
  const line = new THREE.Line(geometry, material);

  scene.add(line);

  // 来个小球放在原点
  const geometry2 = new THREE.SphereGeometry(1);
  const material2 = new THREE.MeshBasicMaterial({ color: 0x0000ff });
  const sphere = new THREE.Mesh(geometry2, material2);
  scene.add(sphere);

  let camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 10000);
  camera.position.set(40, 40, 40);
  camera.lookAt(0, 0, 0);

  // 坐标系
  const axesHelper = new THREE.AxesHelper(100);
  scene.add(axesHelper);

  let renderer = new THREE.WebGLRenderer();
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.getElementById('canvas').appendChild(renderer.domElement);

  function animate() {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);
    // 让小球在那一根线上移动
    const t = (Date.now() * 0.001) / 10;
    const position = curve.getPointAt(t % 1);
    sphere.position.copy(position);

    // 让相机视角跟随小球

    camera.position.copy(sphere.position);
    // 根据移动方向决定相机的朝向
    camera.position.add(new THREE.Vector3(20, 0, 0));
    camera.lookAt(sphere.position);
  }
  animate();
});
</script>