更新:添加了一个jsfiddle来说明:
我有一个放置在场景中的对象(立方体)。我的目标是提供3个角度,代表现实世界中物体的方向。角度将根据实际的X,Y和Z轴进行测量。我失败(悲惨)是如何给对象这些角度,然后随着数据的变化,设置对象以接收新的三角形。我似乎发现的是,当我设置一组初始角度时,一切都很好,但是当我设置一组新的角度时,它们似乎相对于局部对象空间方向而不是世界空间进行设置。我试图了解这个领域中提出的一些类似问题,但这些问题似乎是围绕轴旋转物体,而不是明确地将物体的角度设置在世界轴上。
当我的x,y或z角度的值发生变化时,我正在调用:
cube.rotation.set(x, y , z, 'XYZ');
为了进一步说明,这里是我的立方体的屏幕截图...没有改变X或Y,我绕Z旋转90 ...看到之前和之后的两个图像
和
注意旋转是如何围绕紫色面的法线方向发生的,而不是围绕世界Z轴...
我难过: - (
答案 0 :(得分:7)
cube.rotation.set()
,正如您已经发现的那样,沿着对象的局部轴创建一个旋转。在第一次旋转期间,例如,沿着X,世界坐标中的局部X轴恰好等于世界X轴。然而,在旋转之后,世界坐标中的局部Y轴和Z轴不再等于世界Y轴和Z轴。因此,沿着这些轴的后续旋转将不再像沿世界轴的旋转那样。如果更改旋转顺序,则只会在本地空间中发生第一次旋转,该空间偶然与世界空间相同。所有后续旋转将在不再相同的局部空间中计算。
您想要旋转对象三次:沿世界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旋转为零且Z旋转始终为Y时,Y旋转沿世界Y轴旋转沿着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滑块的绝对值的含义。但是,如果这些值不具有任何意义,那么可以使用什么来将对象的当前轮换保存为 旋转?根据我的经验,最好只存储对象的转换矩阵批发。也就是说,不要试图保存三个特定的旋转角度,加上平移向量,加上可能的缩放参数,只需将整个矩阵保存为对象的一部分。
答案 1 :(得分:1)
您必须向场景添加一个轴心点,这将是旋转的中心。必须将立方体混搭添加到枢轴点,然后才能旋转轴心点。
我在这里更新了你的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');