Three.js中无人机螺旋桨无法绕自身中心旋转的问题求助
问题:Three.js中GLTF螺旋桨模型绕外部轴旋转而非自身中心
问题描述
在Three.js中实现无人机螺旋桨旋转效果,螺旋桨来自GLTF模型,已将其放置在pivot枢轴组后加入总组,但螺旋桨并未绕自身中心旋转,而是绕外部轴转动。控制台显示螺旋桨mesh在pivot中的位置为(0,0,0),尝试调整枢轴位置等方案后问题仍未解决。
加载代码
const loader = new THREE.GLTFLoader(); const p1Meshes = []; const p2Meshes = []; const mMesh = []; const models = [ { path: 'zep-main-1.gltf', center: new THREE.Vector3(40, -80, 250), scale: 100, materialProps: { color: 0x999966, transparent: true, opacity: 0.96, roughness: 0.8, metalness: 0.4, side: THREE.DoubleSide, }, reflection: false, reflectionX: 0, pivot: false, }, { path: 'zep-p1.gltf', center: new THREE.Vector3(40-4.5, -80, 250), scale: 100, materialProps: { color: 0x448888, transparent: true, opacity: 0.98, roughness: 0.2, metalness: 0.2, side: THREE.DoubleSide, }, reflection: true, reflectionX: 41, pivot: true, } // 其余模型配置省略 ]; const compositeModel = new THREE.Group(); function loadModel(loader, path, center, scale, materialProps, reflection = false, reflectionX = 0, pivot=false) { return new Promise((resolve, reject) => { loader.load( path, function (gltf) { const model = gltf.scene; camera.lookAt(center); if(pivot){ var box = new THREE.Box3().setFromObject( model ); box.getCenter( model.position ); model.position.multiplyScalar( - 1 ); const pivot = new THREE.Group(); pivot.position.sub(center); compositeModel.add(pivot); pivot.add(model) } else { model.position.sub(center); compositeModel.add(model); } model.scale.set(scale, scale, scale); model.traverse(function (child) { if (child.isMesh) { child.material = new THREE.MeshStandardMaterial(materialProps); } }); if (path.includes('zep-p1')) { p1Meshes.push(model); } else if (path.includes('zep-p2')) { p2Meshes.push(model); } if(path.includes('zep-main-1')|| path.includes('zep-mw')) mMesh.push(model); if (reflection) { if(pivot){ const reflectedModel = model.clone(); reflectedModel.scale.x = -scale; var box = new THREE.Box3().setFromObject( reflectedModel ); box.getCenter( reflectedModel.position ); reflectedModel.position.multiplyScalar( - 1 ); const rpivot = pivot.clone(); rpivot.scale.x = -scale; rpivot.position.x -= reflectionX; compositeModel.add(rpivot); rpivot.add(reflectedModel) } else { const reflectedModel = model.clone(); reflectedModel.scale.x = -scale; reflectedModel.position.x -= reflectionX; compositeModel.add(reflectedModel); } if (path.includes('zep-p1') ) { p1Meshes.push(reflectedModel); } else if (path.includes('zep-p2')) { p2Meshes.push(reflectedModel); } } resolve(); }, function (xhr) { console.log((xhr.loaded / xhr.total * 100) + '% loaded'); }, function (error) { console.error('An error happened', error); reject(error); } ); }); }
旋转代码
function startAirship() { var rotationSpeed = 0.1; function animateAirshipProps() { requestAnimationFrame(animateAirshipProps); mMesh.forEach( m => { console.log(m.position); }) p1Meshes.forEach(mesh => { console.log("mesh position before: ", mesh.position, mesh.parent.position, mesh.parent.parent, mesh); mesh.rotation.z=rotationSpeed; rotationSpeed = rotationSpeed + 0.01; }); p2Meshes.forEach(mesh => { //mesh.position.sub(point); //mesh.rotation.z -= rotationSpeed; //mesh.position.add(point); }); renderer.render(scene, camera); } animateAirshipProps(); }
解决方案
核心问题分析
- 变换顺序错误:Three.js中变换优先级为「缩放→旋转→平移」,原代码先调整模型位置再应用缩放,导致模型中心点被缩放偏移。
- 枢轴位置计算错误:原代码中
pivot.position.sub(center)会将枢轴定位到错误位置,而非模型的目标中心。 - 反射模型枢轴克隆问题:克隆原枢轴会继承错误的变换属性,导致反射模型旋转中心偏移。
修改后的关键代码
1. 修正loadModel中的枢轴与缩放逻辑
if(pivot){ // 先应用缩放,再计算包围盒 model.scale.set(scale, scale, scale); const box = new THREE.Box3().setFromObject(model); const modelCenter = new THREE.Vector3(); box.getCenter(modelCenter); // 将模型原点移到自身几何中心 model.position.sub(modelCenter); // 创建枢轴并定位到目标位置 const pivot = new THREE.Group(); pivot.position.copy(center); compositeModel.add(pivot); pivot.add(model); } else { model.scale.set(scale, scale, scale); model.position.sub(center); compositeModel.add(model); }
2. 修正反射模型的枢轴创建逻辑
if (reflection) { if(pivot){ const reflectedModel = model.clone(); reflectedModel.scale.set(scale, scale, scale); reflectedModel.scale.x = -scale; const box = new THREE.Box3().setFromObject(reflectedModel); const modelCenter = new THREE.Vector3(); box.getCenter(modelCenter); reflectedModel.position.sub(modelCenter); // 重新创建枢轴,避免克隆原枢轴的错误变换 const rpivot = new THREE.Group(); rpivot.position.copy(center); rpivot.position.x += reflectionX; // 调整反射后的X轴位置 compositeModel.add(rpivot); rpivot.add(reflectedModel); } else { const reflectedModel = model.clone(); reflectedModel.scale.set(scale, scale, scale); reflectedModel.scale.x = -scale; reflectedModel.position.x -= reflectionX; compositeModel.add(reflectedModel); } }
3. 优化旋转逻辑
避免直接赋值旋转角度,改为累加保证持续旋转:
p1Meshes.forEach(mesh => { mesh.rotation.z += 0.1; // 固定速度累加旋转角度 });
内容的提问来源于stack exchange,提问作者Sean
相关产品推荐
相关产品推荐

