设置对象绕世界轴的绝对旋转
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');
【讨论】:
这不会保持立方体的原始世界旋转。以上是关于设置对象绕世界轴的绝对旋转的主要内容,如果未能解决你的问题,请参考以下文章