当旋转角度超过90度时,Three.js的setFromRotationMatrix方法表现奇怪。

9
我有一些物体,每个旋转轴都有一个单独的父级(X-rotation有一个,Y-rotation有一个,Z-rotation也有一个)。它们之间也都是相互关联的:X-rotation对象是Y-rotation对象的子对象,而Y-rotation对象又是Z-rotation对象的子对象。
我试图创建一个功能,使用户可以将场景中的所有对象一起旋转(它们都包含在单个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

2
我已经两次阅读了您的问题描述,但实际上无法理解您试图实现什么。但是有一件事情让我印象深刻,那就是使用欧拉角绕所有轴旋转物体时出现的问题。当您使用欧拉角旋转时遇到奇怪的行为时,可能是由于Gimbal Lock引起的。我建议尝试改用四元数来解决这个问题。Three.js也支持它们。 - micnil
我能理解你为什么觉得做这些似乎有点多余,但是作为参考,这是一个页面的地址:https://mrgarretto.com/model/。这是一个供用户使用的工具,在用户使用该工具创建项目后,必须能够生成各种内容。我制作了一个相册,可以帮助展示问题的具体情况:http://imgur.com/a/jkTeo。我将尝试使用四元数看看是否能解决这个问题。 - MrGarretto
1个回答

5
你遇到了万向节锁定问题。使用欧拉角时,你总会遇到万向节锁定问题,并且在应用多个旋转时会遇到意外行为。
例如,在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);

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