How to always rotate from a particular orientation

2019-08-14 02:56发布

(Apologies in advance. My math skills and powers of description seem to have left me for the moment) Imagine a cube on screen with a two sets of controls. One set of controls to rotate the cube side to side (aka yaw or Y or even Z depending on one's mathematical leanings) and another set of controls to rotate up and down (aka pitch or X).

What I would like to do is make it so that the two set sof controls always rotate the cube in relation to the viewer / screen irrespective of how the cube is currently rotated.

A regular combination of either matrix or quaternion based rotations doesn't achieve this effect because the rotations get applied in a serial fashion (with each rotation "starting" from where the previous one left off).

e.g. With the psuedo code of combinatedRotation = RotateYaw(90) * RotatePitch(45) will give me a cube that appears to be "rolling" to one side because the Pitch rotation has been rotated as well. (or for a more dramatic example RotateYaw(180) * RotatePitch(45) will produce a cube where it appears the the pitch is working in reverse to the screen)

Can somebody either point me to or supply the correct way to make the two rotations independant from each other in effect so that irrespective of how the cube is currently rotated Yaw and Pitch work "as expected" in relation to the on screen controls?

标签: math 3d
1条回答
Viruses.
2楼-- · 2019-08-14 03:16

EDIT 3: It just occurred to me that the solution below, while correct, is unnecessarily complicated. You can achieve the same effect by simply multiplying the rotation matrix by the orientation matrix to compute the new orientation:

M = R * M

Though not relevant to the question, this would also correctly handle orientation matrices that aren't made up of pure rotation, but also contain translation, skew, etc.

(End of edit 3)


You need a transform matrix comprising the current rotated axes of your object's local coordinate system. You then apply rotations to that matrix.

In mathematical terms, you start with an identity matrix as follows:

M = [1  0  0  0]
    [0  1  0  0]
    [0  0  1  0]
    [0  0  0  1]

This matrix comprises three vectors, U, V and W, that represent — in world coordinates — the three unit vectors of your object's local coordinate system:

M = [Ux Vx Wx 0]
    [Uy Vy Wy 0]
    [Uz Vz Wz 0]
    [0  0  0  1]

When you want to rotate the object, rotate each vector in situ. In other words, apply the rotation independently to each of U, V and W within the matrix.

When rendering, simply apply M as a single transform to your object. (In case you're wondering, don't apply the rotations themselves; just the matrix.)

EDIT 2: (Appears before the first edit, since it provides context for it.)

On revisiting this answer long after it was originally posted, I've realised that I might not have picked up on a misunderstanding that you might have about how to apply rotations from each control.

The idea is not to accumulate the rotation to be applied by each control and apply them separately. Rather, you should apply each incremental rotation (i.e., every time one of your slider controls fires a change event) immediately to the U, V and W vectors.

To put this in more concrete terms, don't say, "In total, the vertical control has moved 47° and the horizontal control has moved -21°" and apply them as two big rotations. That will exhibit the same problem that motivated your question. Instead, say, "The vertical slider just moved 0.23°", and rotate U, V and W about the X-axis by 0.23°.

In short, the 90° yaw followed by 45° pitch described below is probably not what you want.

EDIT: As requested, here's how the case of 90° yaw followed by 45° pitch pans out in practice...

Since you start with the identity matrix, the basis vectors will simply be your world unit vectors:

U = [1]  V = [0]  W = [0]
    [0]      [1]      [0]
    [0]      [0]      [1]

To apply the 90° yaw, rotate each basis vector around the Z-axis (reflecting my mathematical leaning), which is almost trivial:

U = [0]  V = [-1]  W = [0]
    [1]      [ 0]      [0]
    [0]      [ 0]      [1]

Thus, after the 90° yaw, the transform matrix will be:

M = [0 -1  0  0]
    [1  0  0  0]
    [0  0  1  0]
    [0  0  0  1]

Applying this matrix to the subject will achieve the desired 90° rotation around Z.

To then apply a 45° pitch (which I'll take to be around the X-axis), we rotate our new basis vectors in the YZ-plane, this time by 45°:

U = [0  ]  V = [-1]  W = [ 0  ]
    [0.7]      [ 0]      [-0.7]
    [0.7]      [ 0]      [ 0.7]

Thus, the new transform matrix is:

M = [0    -1   0    0]
    [0.7   0  -0.7  0]
    [0.7   0   0.7  0]
    [0     0   0    1]

If you multiply the two rotations together:

Yaw(90)*Pitch(45) = [0 -1 0 0]*[1  0    0    0] = [0  -0.7  0.7  0]
                    [1  0 0 0] [0  0.7 -0.7  0]   [1   0    0    0]
                    [0  0 1 0] [0  0.7  0.7  0]   [0   0.7  0.7  0]
                    [0  0 0 1] [0  0    0    1]   [0   0    0    1]

Pitch(45)*Yaw(90) = [1  0    0    0]*[0 -1 0 0] = [0    -1   0    0]
                    [0  0.7 -0.7  0] [1  0 0 0]   [0.7   0  -0.7  0]
                    [0  0.7  0.7  0] [0  0 1 0]   [0.7   0   0.7  0]
                    [0  0    0    1] [0  0 0 1]   [0     0   0    1]

You'll notice that the second form is the same as the transform matrix produced by manipulating the basis vectors, but this is just a coincidence (and quite a common one when mixing up 90° and 45° rotations). In the general case, neither order of application will match the basis-transform.

I've run out of steam, so I hope the explanation so far makes things clearer, not muddier.

查看更多
登录 后发表回答