设置对象绕世界轴的绝对旋转 [英] Set an object's absolute rotation around the world axis

查看:57
本文介绍了设置对象绕世界轴的绝对旋转的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

我被难住了:-(

解决方案

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 旋转 = 新的 THREE.Matrix4();旋转.乘法(新三.矩阵4().makeRotationX(x));var worldYAxis = new THREE.Vector3(0, 1, 0).applyMatrix4(new THREE.Matrix4().getInverse(rotation));var rotationWorldY = new THREE.Matrix4().makeRotationAxis(worldYAxis, y);旋转.乘法(rotationWorldY);var worldZAxis = new THREE.Vector3(0, 0, 1).applyMatrix4(new THREE.Matrix4().getInverse(rotation));var rotationWorldZ = new THREE.Matrix4().makeRotationAxis(worldZAxis, z);旋转.乘法(旋转世界Z);cube.matrixAutoUpdate = false;cube.matrix = 旋转;

如果您尝试此代码,您会注意到当 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;函数 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 旋转 = 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);旋转.乘法(旋转世界X);var worldYAxis = new THREE.Vector3(0, 1, 0).applyMatrix4(new THREE.Matrix4().getInverse(rotation));var rotationWorldY = new THREE.Matrix4().makeRotationAxis(worldYAxis, y - prevY);旋转.乘法(rotationWorldY);var worldZAxis = new THREE.Vector3(0, 0, 1).applyMatrix4(new THREE.Matrix4().getInverse(rotation));var rotationWorldZ = new THREE.Matrix4().makeRotationAxis(worldZAxis, z - prevZ);旋转.乘法(旋转世界Z);上一个X = x;上一个 = y;prevZ = z;cube.matrixAutoUpdate = false;cube.matrix = 旋转;}

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

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

Update: Added a jsfiddle to illustrate:

JSFiddle

I have an object ( a cube ) that is placed in the scene. My goal is to be supplied 3 angles which represent an object's orientation in the real world. The angles will be measured against real-world X, Y and Z axis. What I am failing (miserably) is how to give an object these angles and then as the data changes, set the object to receive a new triplet of angles. What I seem to be finding is that when I set an initial set of angles, all is good, but when I set a new set of angles, they appear to be set relative to the local object space orientation as opposed to world space. Ive tried to understand some of the similar questions posed in this area but these appear to be about rotating an object around an axis as opposed to setting an object's angles explicitly against the world axis.

When a value of my x, y or z angle changes, I am currently calling:

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

To illustrate further, here is a screen shot of my cube ... without changing X or Y, I rotate 90 about Z ... see the the two images of before and after

and

Notice how the rotation has occurred around the direction of the normal of the purple face and not around the world Z axis ...

I'm stumped :-(

解决方案

cube.rotation.set(), as you've already discovered, creates a rotation along the local axes of the object. During the first rotation, say, along X, the local X axis in world coordinates is exactly equal to the world X axis. After the rotation however, the local Y and Z axes in world coordinates are no longer equal to the world Y and Z axes. Therefore, subsequent rotations along those axes will no longer look like rotations along the world axes. If you change the order of rotations, again, only the first rotation will take place in local space that is incidentally identical to world space. All subsequent rotations will be calculated in local space that is no longer identical.

You want to rotate your object three times: once along the world X axis, once along the world Y axis and once along the world Z axis. But we now know that all rotations take place in local space. The key to do the rotations in world space is to ask the question: "What does this world axis look like in the local space of my object?" If you can take a world axis, express it as a vector in the object's local space, and then make a rotation along that vector, then you've got yourself a rotation along a world vector, regardless of the specific local space.

Let's integrate that idea into your 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;

If you try this code, you'll notice that X rotations are along the world X axis when Y and Z rotations are zero, Y rotations are along the world Y axis when Z rotation is zero and Z rotations are always along the Z axis.

But what if, after setting a X, then a Y, then a Z rotation, we turn the X slider again? You'll notice that the rotation is no longer along the world X axis, but along the local X axis. The reason for this is simple, although the explanation why it's so may not be. With one word - rotations do not commute. You see, the rotations X(a) * Y(b) * Z(c) * X(d) is not in general equal to X(a + d) * Y(b) * Z(c). That is, if you rotate along world X by a, then along world Y by b, then along world Z by c, and then again along world X by d, then the result is not the same as the rotation that you would have gotten if you had rotated along world X by a + d right from the start. The reason for that is that "world X" at the start of the transformations and "world X" at the end of the transformations are two different vectors, whose values depend on all transformations done up to this point. Therefore, they represent different axes of rotation in local space.

I will try to anticipate the next question: "How can I make it so, that no matter how I move my sliders left and right, the object always rotates only along the world axes?" The answer to that build on what we already know about doing rotations around world axes - the world axis must be expressed as a vector in local space and the the rotation must take place around that vector. An implementation of the idea follows:

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/

The above function is bound as the slide event handler of all sliders: "slide": doRotate. Now, if you try to rotate hither and thither, you'll notice that no matter the current orientation of the box, moving the slider always results in rotations along the world axes. There is one caveat though: the values of the sliders no longer represent actual angles of rotation around any particular axis. You'll notice that the doRotate function only applies a delta rotation along the world axes as they're currently expressed in local coordinates. We have achieved the goal of being able to apply incremental rotations that are always done along the world axes, but we have lost the meaning of the absolute values of the X, Y, and Z sliders. But if the values don't mean anything, what can possibly be used to save the object's current rotation as the rotation? In my experience, it's best to just store the object's transformation matrix wholesale. That is, instead of trying to save three particular rotation angles, plus a translation vector, plus maybe scaling parameters, just save the entire matrix as part of the object.

这篇关于设置对象绕世界轴的绝对旋转的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆