设置对象绕世界轴的绝对旋转

Posted

技术标签:

【中文标题】设置对象绕世界轴的绝对旋转【英文标题】:Set an object's absolute rotation around the world axis 【发布时间】:2016-10-20 14:45:01 【问题描述】:

更新:添加了一个jsfiddle来说明:

JSFiddle

我有一个放置在场景中的对象(立方体)。我的目标是提供 3 个角度来代表现实世界中对象的方向。角度将根据真实世界的 X、Y 和 Z 轴进行测量。我失败的(悲惨地)是如何给一个对象这些角度,然后随着数据的变化,将对象设置为接收一个新的三元组角度。我似乎发现的是,当我设置一组初始角度时,一切都很好,但是当我设置一组新角度时,它们似乎是相对于本地对象空间方向而不是世界空间设置的。我试图理解该领域提出的一些类似问题,但这些问题似乎是关于围绕轴旋转对象,而不是明确设置对象的角度相对于世界轴。

当我的 x、y 或 z 角度的值发生变化时,我正在调用:

cube.rotation.set(x, y , z, 'XYZ');

为了进一步说明,这是我的立方体的屏幕截图...在不更改 X 或 Y 的情况下,我围绕 Z 旋转 90...查看之前和之后的两个图像

注意旋转是如何围绕紫色面的法线方向发生的,而不是围绕世界 Z 轴发生的......

我被难住了:-(

【问题讨论】:

看看***.com/questions/14774633/…和***.com/questions/19385534/…对你有没有帮助。 测试 ... ...我认为当我从 cube.rotation.set(x, y , z, 'XYZ'); 更改代码时它起作用了到 cube.rotation.set(x, y , z, 'ZYX');但它只是将问题转移到了另一个轴上。 添加了一个 jsFiddle 来说明。 【参考方案1】:

cube.rotation.set(),正如您已经发现的那样,沿对象的局部轴创建旋转。在第一次旋转期间,例如,沿 X 轴,世界坐标中的局部 X 轴与世界 X 轴完全相等。然而,在旋转之后,世界坐标中的局部 Y 和 Z 轴不再等于世界 Y 和 Z 轴。因此,沿这些轴的后续旋转将不再看起来像沿世界轴的旋转。如果您再次更改旋转顺序,则只有第一次旋转将发生在偶然与世界空间相同的局部空间中。所有后续旋转都将在不再相同的局部空间中计算。

您想要将对象旋转 3 次:一次沿着世界 X 轴,一次沿着世界 Y 轴,一次沿着世界 Z 轴。但是我们现在知道所有的旋转都发生在局部空间中。在世界空间中进行旋转的关键是要问一个问题:“这个世界轴在我的对象的局部空间中是什么样子的?”如果您可以取一个世界轴,将其表示为对象局部空间中的向量,然后沿该向量进行旋转,那么您就可以沿世界向量进行旋转,而不管特定的局部空间如何。

让我们把这个想法融入你的小提琴:

var x = toRadians($("#rotateX").slider("option", "value"));
var y = toRadians($("#rotateY").slider("option", "value"));
var z = toRadians($("#rotateZ").slider("option", "value"));

var rotation = new THREE.Matrix4();
rotation.multiply(new THREE.Matrix4().makeRotationX(x));

var worldYAxis = new THREE.Vector3(0, 1, 0).applyMatrix4(new THREE.Matrix4().getInverse(rotation));
var rotationWorldY = new THREE.Matrix4().makeRotationAxis(worldYAxis, y);
rotation.multiply(rotationWorldY);

var worldZAxis = new THREE.Vector3(0, 0, 1).applyMatrix4(new THREE.Matrix4().getInverse(rotation));
var rotationWorldZ = new THREE.Matrix4().makeRotationAxis(worldZAxis, z);
rotation.multiply(rotationWorldZ);

cube.matrixAutoUpdate = false;
cube.matrix = rotation;

如果您尝试此代码,您会注意到当 Y 和 Z 旋转为零时,X 旋转沿世界 X 轴,当 Z 旋转为零时,Y 旋转沿世界 Y 轴,而 Z 旋转始终沿Z轴。

但是,如果在设置了 X、Y、Z 旋转之后,我们再次转动 X 滑块会怎样?您会注意到旋转不再沿世界 X 轴,而是沿局部 X 轴。原因很简单,尽管可能无法解释为什么会这样。一句话—​​—轮班不通勤。你看,旋转X(a) * Y(b) * Z(c) * X(d) 通常不等于X(a + d) * Y(b) * Z(c)。也就是说,如果你沿着世界 X 旋转 a,然后沿着世界 Y 旋转 b,然后沿着世界 Z 旋转 c,然后再沿着世界 X 旋转 d,那么结果就不一样了如果你从一开始就沿着世界 X 旋转 a + d,你会得到的旋转。原因是变换开始时的“世界 X”和变换结束时的“世界 X”是两个不同的向量,它们的值取决于到目前为止所做的所有变换。因此,它们代表局部空间中不同的旋转轴。

我将尝试预测下一个问题:“我怎样才能做到,无论我如何左右移动滑块,对象总是只沿着世界轴旋转?”答案建立在我们已经知道的围绕世界轴旋转的基础上——世界轴必须表示为局部空间中的向量,并且旋转必须围绕该向量进行。该想法的实现如下:

var prevX = 0, prevY = 0, prevZ = 0;
function doRotate() 
    var x = toRadians($("#rotateX").slider("option", "value"));
    var y = toRadians($("#rotateY").slider("option", "value"));
    var z = toRadians($("#rotateZ").slider("option", "value"));

    var inverse = new THREE.Matrix4().getInverse(cube.matrix);

    var rotation = cube.matrix;
    var worldXAxis = new THREE.Vector3(1, 0, 0).applyMatrix4(new THREE.Matrix4().getInverse(rotation));
    var rotationWorldX = new THREE.Matrix4().makeRotationAxis(worldXAxis, x - prevX);
    rotation.multiply(rotationWorldX);
    var worldYAxis = new THREE.Vector3(0, 1, 0).applyMatrix4(new THREE.Matrix4().getInverse(rotation));
    var rotationWorldY = new THREE.Matrix4().makeRotationAxis(worldYAxis, y - prevY);
    rotation.multiply(rotationWorldY);
    var worldZAxis = new THREE.Vector3(0, 0, 1).applyMatrix4(new THREE.Matrix4().getInverse(rotation));
    var rotationWorldZ = new THREE.Matrix4().makeRotationAxis(worldZAxis, z - prevZ);
    rotation.multiply(rotationWorldZ);

    prevX = x;
    prevY = y;
    prevZ = z;

    cube.matrixAutoUpdate = false;
    cube.matrix = rotation;

小提琴:https://jsfiddle.net/2whv0e8o/13/

上述函数绑定为所有滑块的slide事件处理程序:"slide": doRotate。现在,如果您尝试左右旋转,您会注意到无论框的当前方向如何,移动滑块总是会导致沿世界轴旋转。但是有一个警告:滑块的值不再代表围绕任何特定轴的实际旋转角度。您会注意到doRotate 函数仅沿世界轴应用增量旋转,因为它们当前以局部坐标表示。我们已经实现了能够应用始终沿世界轴完成的增量旋转的目标,但是我们失去了 X、Y 和 Z 滑块绝对值的含义。但是,如果这些值没有任何意义,那么可以使用什么来将对象的当前旋转保存为 the 旋转?根据我的经验,最好只批发存储对象的变换矩阵。也就是说,与其尝试保存三个特定的旋转角度、一个平移向量,还可能加上缩放参数,只需将整个矩阵保存为对象的一部分。

【讨论】:

错误:将 X 滑到 75%,然后将 Y 滑到 75%。然后将 X 移回 50%,然后将 Y 移回 50%。滑块回到了它们开始的位置,但形状不是...... 它按预期工作。形状不会在您开始的地方结束,因为第二次 X 旋转不会撤消第一次 X 旋转,因为旋转现在沿着不同的轴 w.r.t 发生。对象。 嗨@StefanDragnev,你的意思是写X(d) * Z(c) * Y(b) * X(a),因为旋转矩阵是从右到左应用的?也许您的意思不是矩阵乘法? 我使用行优先约定(如在 DirectX 中),以便从左到右应用转换。但你是对的,我应该在我的说明中澄清这一点。【参考方案2】:

您必须在场景中添加一个轴心点,这将是旋转的中心。立方体混搭必须添加到枢轴点,然后您可以旋转枢轴点。

我在这里更新了你的 jsFiddle:https://jsfiddle.net/x5w31f33/

var cube = new THREE.Mesh( geometry, material );
cube.position.set(2, 0, 0);

var pivotPoint = new THREE.Object3D();
scene.add( pivotPoint );
pivotPoint.add(cube);

pivotPoint.rotation.set(x, y , z, 'XYZ');

【讨论】:

这不会保持立方体的原始世界旋转。

以上是关于设置对象绕世界轴的绝对旋转的主要内容,如果未能解决你的问题,请参考以下文章

unity 对象旋转,自转

绕单轴的旋转矩阵

RPY roll pitch yaw

从左/右和上/下移动旋转和对象

将围绕任意轴的旋转解析为围绕 X、Y 和 Z 轴的旋转

OpenGL 相对于对象轴的旋转