更新:添加了一个 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 度……请参阅之前和之后的两个图像
和
注意旋转是如何围绕紫色面的法线方向而不是围绕世界 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/