在three.js中,围绕一个点旋转物体的正确方法是什么?

25

大多数关于three.js的教程和问题建议使用three.js绕点旋转对象的方法是创建一个位于旋转中心的父对象,将您的对象附加到该父对象,然后移动子对象。然后当父对象旋转时,子对象就会围绕该点旋转。例如:

//Make a pivot
var pivot = new THREE.Object3D();
//Make an object
var object = new THREE.Mesh( new THREE.BoxGeometry(2,2,2), new THREE.MeshBasicMaterial() );
//Add object to pivot
pivot.add(object);

//Move object away from pivot
object.position.set(new THREE.Vector3(5,0,0));

//rotate the object 90 degrees around pivot
pivot.rotation.y = Math.PI / 2;

这种方法有效,但非常有限-如果您想围绕不同的点旋转对象,则无法实现,因为一个对象不能有两个父级。

下一种可行的方法是将旋转矩阵应用于对象的位置,例如;

//Make an object
var object = new THREE.Mesh( new THREE.BoxGeometry(2,2,2), new THREE.MeshBasicMaterial() );
//Move the object
object.position.set(new THREE.Vector3(5,0,0);

//Create a matrix
var matrix = new THREE.Matrix4();
//Rotate the matrix
matrix.makeRotationY(Math.PI / 2);

//rotate the object using the matrix
object.position.applyMatrix4(matrix);

再次强调,这种方法可行,但似乎没有一种方法可以设置旋转点。 它始终是世界的原点。

另一个选项是在sceneUtils中使用实用程序方法将子对象从父对象中分离出来,并附加新的父对象的方法,但是对此的文档警告不要使用它,并且在任何示例代码中都没有使用它。

最终,我想能够使立方体沿着S形路径移动,但是如果没有(看起来)基本的东西,我会发现这很困难。

tl;dr 我想绕不同点旋转three.js对象。 应该如何处理?


2
看看这个是否有帮助:https://dev59.com/Fo3da4cB1Zd3GeqP7P3s#32038265 - WestLangley
那很有帮助。谢谢。 - Chris
4个回答

9
这个答案可能有所帮助:Three JS Pivot point 解决这个问题的方法有几种。如你所述,可以通过父级方法交换枢轴点,但需要大量工作,将对象位置在不同坐标系之间进行转换。
相反,您可以始终从以下方式考虑(伪代码):
  1. 将对象移动到枢轴点。
  2. 1.1. 从原始位置中减去枢轴点。

  3. 应用旋转。
  4. 通过相同的位置向量将对象移回。
  5. 1.1. 将枢轴点添加到您的对象的新位置中。

注意:所有内容都在世界坐标系中,因此您可能需要适当地进行转换。

这是我如何围绕点旋转的方法。我会从我的位置中减去枢轴,通过 THREE.Quaternion().setFromAxisAngle() 应用四元数旋转,然后再加上枢轴,它非常有效。 - Xander Luciano
1
仅供参考,THREE.Vector3.applyAxisAngle 内部使用四元数,因此您可能不需要创建自己的(除非您需要多次执行相同的操作)。 - TheJim01
我正在使用LineSegment,因此我有2个点正在旋转,但对于未来的读者来说也很好记在心里。 - Xander Luciano

8
简单的解决方案是:移动到锚点,旋转,然后返回。
假设您希望围绕锚点旋转“相机”(此处的“相机”是THREE.Camera的实例,“锚点”是THREE.Vector3的实例):
/// step 1: calculate move direction and move distance:
let moveDir = new THREE.Vector3(
    anchorPoint.x - camera.position.x,
    anchorPoint.y - camera.position.y,
    anchorPoint.z - camera.position.z
);
moveDir.normalize();
let moveDist = camera.position.distanceTo(anchorPoint);
/// step 2: move camera to anchor point
camera.translateOnAxis(moveDir, moveDist);
/// step 3: rotate camera
camera.rotateX(valX);
camera.rotateY(valY);
camera.rotateZ(valZ);
/// step4: move camera along the opposite direction
moveDir.multiplyScalar(-1);
camera.translateOnAxis(moveDir, moveDist);

1
这种方法可行,但极其受限——如果您想围绕不同的点旋转对象,则无法实现,因为对象不能有两个父级。
这是正确的,但我想知道您希望在具有两个父级时作为默认行为的期望是什么。您可以嵌套物品-例如,由女人驾驶汽车的相机具有2个父级:相机->女人->汽车。但是,如果您想要一些平行的父母关系(?)假设女人拿着相机,但您还想让一个男人同时拿着相机。如何使男人转身并拍摄某些东西而不从女人手中拿走相机或者反之亦然?当您尝试想象默认情况下会发生什么时,多个父级的概念就会瓦解。女人是否随着相机移动?那么在这种情况下,相机实际上是否是她的父级?
如果您想要更改父级,则始终可以将对象分离,然后附加到另一个对象上。它会警告您不要手动分离,因为对象将从本地空间转移到其父级的世界空间/新父级的本地空间。

幸运的是,场景工具中有一些专门用于此目的的功能:即附加和分离。因此,当您想要将一个对象交接给另一个对象而不需要转换到世界坐标系并进行奇怪的传送时,可以使用附加/分离函数。

请注意,除非新父级在移交时与旧父级完全相同,否则这将会产生奇怪的结果,因为我们不能保证对象与父级之间的关系,除非在移交时它们保持不变。enter image description here


0

围绕其他对象旋转物体的关键是将要旋转的物体作为被旋转对象的子对象。

假设您已经设置好了基本元素(场景、相机和光源),并且有一个空场景,您可以创建一个可见对象(例如立方体)和一个空对象。

// making a cube
const geometry = new THREE.BoxGeometry(5,5,5);
const material = new THREE.MeshStandardMaterial({roughness:1});
const cube = new THREE.Mesh(geometry, material);

// making an empty point
const point = new THREE.Object3D();

现在我们有一个立方体和一个空点。我们希望该立方体围绕该点旋转(我们无法看到该点)。问题在于,该点和立方体处于同一位置,因此我们必须重新定位立方体,使其与该点相距一定距离。 为此,我们可以添加:
cube.position.setX(20);

记住,只需将父对象添加到场景中,因为它已经包含了子对象,所以不需要单独添加(这会破坏代码)

scene.add(point);

现在,将立方体设置为点的子对象,这样当点旋转时,立方体也会跟着旋转。
point.add(cube);

最后,围绕它的轴旋转该点。
function _render(){
    point.rotation.y += 0.006;
    window.requestAnimationFrame(_render);
    renderer.render(scene, camera);
  }
_render();

你需要了解子元素和父元素的关系,改变父元素的位置会影响到子元素的位置。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接