三.js设置旋转矩阵超过90度时的奇怪行为

Three.js setFromRotationMatrix strange behavior when rotation is over 90 degrees

本文关键字:90度 设置 js 旋转      更新时间:2023-09-26

>我有一些对象,每个对象对于每个旋转轴都有一个单独的父级(1 个用于 X 旋转,1 个用于 Y 旋转,1 个用于 Z 旋转。它们也都按该顺序相互关联:X 旋转对象是 Y 旋转对象的子对象。Y 旋转对象是 Z 旋转对象的子项)。

我正在尝试制作一个功能,允许用户一起旋转场景中的所有对象(它们都包含在单个 Object3D 中)。旋转 Object3D 时,程序必须找到所有对象的绝对位置和相对于世界的旋转,以便程序可以输出每个对象的新值。

为此,我目前已将其设置为移动对象,以便将其在"场景旋转器"(Object3D)中的位置设置为相对于世界的绝对位置。现在,我试图使对象的旋转成为对象相对于世界的绝对旋转,以便在"场景旋转器"的旋转发生变化时相应地变化。此外,当我尝试在子对象上运行一次 setFromRotationMatrix 方法时,它无法正常工作,因此,我必须为每个父对象再次运行它,并相应地从它们那里获取每个单独的旋转

这是我目前拥有的代码,它应该获得对象相对于世界的绝对旋转:

var beforeRotForX = new THREE.Euler();
beforeRotForX.setFromRotationMatrix(objects[i].parent.matrixWorld, "ZYX");
var beforeRotForY = new THREE.Euler(); // Had to be a separate one for some reason...
beforeRotForY.setFromRotationMatrix(objects[i].parent.parent.matrixWorld, "ZYX");
var beforeRotForZ = new THREE.Euler(); // And apparently this one has to be separate too
beforeRotForZ.setFromRotationMatrix(objects[i].parent.parent.parent.matrixWorld, "ZYX");
// Absolute before rotation
objects[i].userData.sceneBeforeRotAbs = {
    x: beforeRotForX.x,
    y: beforeRotForY.y,
    z: beforeRotForZ.z
};

然后,它必须将该绝对旋转应用于对象的相对旋转

objects[i].parent.rotation.x = objects[i].userData.sceneBeforeRotAbs.x;
objects[i].parent.parent.rotation.y = objects[i].userData.sceneBeforeRotAbs.y;
objects[i].parent.parent.parent.rotation.z = objects[i].userData.sceneBeforeRotAbs.z;

当第二个父项的 Y 旋转在 -90 到 90 之间时,这一切都可以正常工作

// Results of absolute world rotation when the Y-rotation of the
// second parent is set to 90 degrees (1.5707... as euler)
objects[i].userData.sceneBeforeRotAbs.x === 0
objects[i].userData.sceneBeforeRotAbs.y === 1.5707963267948966
objects[i].userData.sceneBeforeRotAbs.z === 0

但是当第二个父项的 Y 旋转低于 -90 或大于 90 时,它给出了绝对世界 X 旋转和 Y 旋转的错误值

// Results of absolute world rotation when the Y-rotation of the
// second parent is set to 91 degrees (1.5882... as euler)
objects[i].userData.sceneBeforeRotAbs.x === 3.141592653589793
objects[i].userData.sceneBeforeRotAbs.y === 1.5533438924131038
objects[i].userData.sceneBeforeRotAbs.z === 0

你遇到了云台锁。使用欧拉角时,您总是会遇到万向节锁定问题,并且在应用多个旋转时会遇到意外行为。

例如,在 2D 空间中,30° 旋转与 -330° 旋转相同。在 3D 空间中,您可能会遇到同样的问题:在 X 轴上旋转对象 180° 与给它 180° Y 轴 + 180° Z 轴旋转相同。

您应该使用四元数声明您的旋转,然后将它们相乘以获得所需的结果,而不会出现万向节锁定问题。

// Declare angles
var angleX = 45;
var angleY = 120;
var angleZ = 78;
// Declare X and Y axes
var axisX = new THREE.Vector3(1, 0, 0);
var axisY = new THREE.Vector3(0, 1, 0);
var axisZ = new THREE.Vector3(0, 0, 1);
// Init quaternions that will rotate along each axis
var quatX = new THREE.Quaternion();
var quatY = new THREE.Quaternion();
var quatZ = new THREE.Quaternion();
// Set quaternions from each axis (in radians)...
quatX.setFromAxisAngle(axisX, THREE.Math.degToRad(angleX));
quatY.setFromAxisAngle(axisY, THREE.Math.degToRad(angleY));
quatZ.setFromAxisAngle(axisZ, THREE.Math.degToRad(angleZ));
// ...then multiply them to get final rotation
quatY.multiply(quatX);
quatZ.multiply(quatY);
// Apply multiplied rotation to your mesh
mesh.quaternion.copy(quatZ);