You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Three.js实现圆柱端点沿自定义路径动画的NEWUPDATE函数需求

实现Three.js圆柱端点沿自定义路径运动的高效方案

问题概述

现有基于Three.js的旋转圆柱实现,通过UPDATE函数将单位圆柱变换为指定两个端点的圆柱。但当需要让圆柱一端沿模长不固定的复杂自定义路径运动时,原旋转变换思路不再适用,需实现NEWUPDATE函数,要求不销毁重建圆柱以保证性能,调用逻辑如下:

const Path = [
  new THREE.Vector3(1, 0, 0),
  new THREE.Vector3(1, 2, 3),
  // 更多自定义路径点
];

let k = 0;
function animate() {
  requestAnimFrame(animate);
  NEWUPDATE(geomCylinder, Path[k]);
  k++;
  render();
}

原参考代码

核心动画与UPDATE函数

const O = new THREE.Vector3(0, 0, 0);
const alpha = Math.PI / 180;
const Malpha = new THREE.Vector3(Math.sin(alpha), Math.cos(alpha), 0);
function animate() {
  requestAnimFrame(animate);
  UPDATE(geomCylinder, O, J);
  UPDATE(geomCylinder, O, Malpha);
  render();
}
const radius = 0.07;
// 单位圆柱:底面中心分别位于(0,0,0)和(0,1,0)
const geomUnitCylinder = new THREE.CylinderGeometry(radius, radius, 1, 64, 1, true);
geomUnitCylinder.translate(0, 0.5, 0);

const J = new THREE.Vector3(0, 1, 0);
// 将单位圆柱变换为端点在P、Q的圆柱
function UPDATE(geomCylinder, P, Q) { 
  const vec = Q.clone().sub(P);
  const height = vec.length();
  vec.normalize();
  const quaternion = new THREE.Quaternion().setFromUnitVectors(J, vec);
  geomCylinder.scale(1, height, 1);
  geomCylinder.applyQuaternion(quaternion);
  geomCylinder.translate(P.x, P.y, P.z);
}

完整场景代码

JavaScript

const radius = 0.07;
// 单位圆柱:底面中心分别位于(0,0,0)和(0,1,0)
const geomUnitCylinder = new THREE.CylinderGeometry(
  radius,
  radius,
  1,
  64,
  1,
  true
);
geomUnitCylinder.translate(0, 0.5, 0);

const J = new THREE.Vector3(0, 1, 0);
// 将单位圆柱变换为端点在P、Q的圆柱
function UPDATE(geomCylinder, P, Q) {
  const vec = Q.clone().sub(P);
  const height = vec.length();
  vec.normalize();
  const quaternion = new THREE.Quaternion().setFromUnitVectors(J, vec);
  geomCylinder.scale(1, height, 1);
  geomCylinder.applyQuaternion(quaternion);
  geomCylinder.translate(P.x, P.y, P.z);
}

// Three.js 场景初始化
const aspect = window.innerWidth / window.innerHeight;
const camera = new THREE.PerspectiveCamera(50, aspect, 1, 10000);
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x111111);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById("container").appendChild(renderer.domElement);
camera.position.z = 5;
scene.add(camera);

function render() {
  renderer.render(scene, camera);
}

const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const geomCylinder = geomUnitCylinder.clone();
const mesh = new THREE.Mesh(geomCylinder, material);
const object = new THREE.Object3D();
object.add(mesh);
scene.add(object);

// 自定义requestAnimFrame实现
window.requestAnimFrame = (() => {
  return (callback) => {
    window.setTimeout(callback, 1000 / 1);
  };
})();

// 原动画逻辑
const O = new THREE.Vector3(0, 0, 0);
const alpha = Math.PI / 180;
const Malpha = new THREE.Vector3(Math.sin(alpha), Math.cos(alpha), 0);
function animate() {
  requestAnimFrame(animate);
  UPDATE(geomCylinder, O, J);
  UPDATE(geomCylinder, O, Malpha);
  render();
}

animate();

HTML

<!-- 引入Three.js 0.151.2版本库文件 -->
<script src="three.min.js"></script>
<div id="container"></div>

解决方案:NEWUPDATE函数实现

核心思路

每次更新前将几何体重置为初始单位圆柱状态,避免多次变换叠加导致的变形;然后根据当前路径点动态计算圆柱的高度、朝向,并完成变换。以下实现默认圆柱一端固定在原点(0,0,0),若需固定其他点,只需修改对应平移参数即可。

NEWUPDATE函数代码

function NEWUPDATE(geomCylinder, targetPoint) {
  // 重置几何体到初始单位圆柱状态
  geomCylinder.copy(geomUnitCylinder);
  
  // 计算原点到目标点的向量
  const vec = targetPoint.clone();
  const height = vec.length();
  
  // 归一化向量,计算旋转四元数(将单位圆柱的Y轴朝向目标方向)
  vec.normalize();
  const quaternion = new THREE.Quaternion().setFromUnitVectors(J, vec);
  
  // 应用缩放、旋转、平移变换
  geomCylinder.scale(1, height, 1);
  geomCylinder.applyQuaternion(quaternion);
  // 若固定点不是原点,替换为固定点坐标即可
  geomCylinder.translate(0, 0, 0);
}

适配后的动画逻辑

// 定义自定义路径
const Path = [
  new THREE.Vector3(1, 0, 0),
  new THREE.Vector3(1, 2, 3),
  new THREE.Vector3(-2, 1, 1),
  new THREE.Vector3(0, -1, 2),
  // 可添加更多路径点
];

let k = 0;
function animate() {
  requestAnimFrame(animate);
  
  // 循环遍历路径点,避免索引越界
  if (k >= Path.length) k = 0;
  
  NEWUPDATE(geomCylinder, Path[k]);
  k++;
  
  render();
}

animate();

关键优化说明

  • 状态重置:通过geomCylinder.copy(geomUnitCylinder)直接复用预定义的单位圆柱,无需重建几何体,保证性能的同时避免累积变换误差。
  • 动态适配:每次根据路径点重新计算圆柱的高度和旋转方向,完美适配任意模长的路径点。
  • 路径循环:添加循环逻辑,实现路径的重复播放。

内容的提问来源于stack exchange,提问作者Stéphane Laurent

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 12:48:08