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

标签 javascript three.js rotation

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

JSFiddle

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

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

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

为了进一步说明,这是我的立方体的屏幕截图……不改变 X 或 Y,我绕 Z 旋转了 90 度……请参阅之前和之后的两个图像

enter image description here



enter image description here

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

我难住了:-(

最佳答案

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

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

让我们把这个想法整合到你的 fiddle 中:

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;
}

fiddle :https://jsfiddle.net/2whv0e8o/13/

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

关于javascript - 设置对象绕世界轴的绝对旋转,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37903979/

相关文章:

javascript - 通过 PHP 将图像文件(不是通过表单选择,只是在一个文件夹中)上传到 mysql?

javascript - ExtJS:是否可以在网格面板中声明绑定(bind)的 if else 条件?

javascript - jquery比较同一行的数据,如果正确则显示第三行的内容

javascript - 使用jquery旋转数组元素

opencv - 单应矩阵分解为旋转矩阵和平移向量

JavaFX 获取旋转 imageView 的角坐标?

javascript - polymer 2 : Handle clicks on PaperCard header

javascript - 沿平坦表面/道路移动带有物体的相机时出现意外旋转

javascript - 如何有效地为多个基于 Canvas 的 THREE.Texture 重用 Canvas

GLSL:找不到我的自定义函数