当旋转超过90度时,Three.js setFromRotationMatrix奇怪的行为

时间:2016-03-19 22:15:52

标签: javascript three.js euler-angles degrees parents

我有每个旋转轴都有一个单独父对象的对象(1表示X旋转,1表示Y旋转,1表示Z旋转。它们也按顺序相互关联:X -rotation对象是Y-rotation对象的子对象.Y-rotation对象是Z-rotation对象的子对象。)

我正在尝试制作一个允许用户一起旋转场景中所有对象的功能(它们都包含在一个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

1 个答案:

答案 0 :(得分:4)

你遇到了gimbal lock。使用欧拉角时,您将始终遇到万向节锁定问题,并且在应用多次旋转时会遇到意外行为。

例如,在2D空间中,30°旋转与-330°旋转相同。在3D空间中,您可以得到同样的问题:在X轴上旋转180°的物体与为其提供180°Y轴+ 180°Z轴旋转相同。

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

// 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);