Rotation Around a Faked Axis

Started by
11 comments, last by NeonWolfTrance 9 years, 6 months ago

I am trying to implement rotation in my engine where rotation around the y-axis (yaw) occurs independent of previous rotations. In other words, if I rotate the pitch forward and the local up axis is now pointing in a different direction, I want my yaw rotation to occur as if the up axis was still in the same location. Here is a picture showing what I am looking to achieve:

[attachment=24245:Untitled-1.png]

Currently, for each object I am storing a persistent matrix that represents all of the translations and rotations that have occurred since it was created. Each frame, this matrix is incrementally updated.

My rotation code looks like this:


	D3DXMatrixRotationYawPitchRoll(&rotation, yaw, pitch, roll);
	D3DXMatrixMultiply(&position, &rotation, &position);

Position is the matrix that persists while the object exists that holds all the rotation and translation info.

This rotation code currently rotates yaw around the objects local y-axis, where I want it to rotate as if the y-axis was still pointing up (0,1,0).

How can I cause the rotation that I am looking for? (shown in the picture next to the check mark)

Advertisement

I'm not sure how you plan to use that approach, but, to answer your question - set yaw to 0 in the YawPitchRoll calc, and calculate a separate Y-axis rotation:


D3DXMatrixRotationYawPitchRoll(&rotation, 0, pitch, roll);
D3DXMATRIX rotY;
D3DXMatrixRotationAxis(&rotY, &D3DXVECTOR3(0,1,0), yaw);
position = rotation * rotY * position;

The resulting matrix is a (roll) rotation about the Z-axis, followed by a (pitch) rotation about the local X-axis, followed by a rotation about the (fixed) Y-axis.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

I'm not sure how you plan to use that approach, but, to answer your question - set yaw to 0 in the YawPitchRoll calc, and calculate a separate Y-axis rotation:


D3DXMatrixRotationYawPitchRoll(&rotation, 0, pitch, roll);
D3DXMATRIX rotY;
D3DXMatrixRotationAxis(&rotY, &D3DXVECTOR3(0,1,0), yaw);
position = rotation * rotY * position;

The resulting matrix is a (roll) rotation about the Z-axis, followed by a (pitch) rotation about the local X-axis, followed by a rotation about the (fixed) Y-axis.

I've tried this same approach without success, it simulates the first picture (just tried it again to make sure). It acts as if you only used the code I have posted :/

As mentioned, I'm not sure what problem you're trying to solve.

How 'bout:


position = rotY * rotation; // perform the Y rotation first

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

As mentioned, I'm not sure what problem you're trying to solve.

How 'bout:


position = rotY * rotation; // perform the Y rotation first

Yea same deal, but sorry missed that part. I'm trying to implement rotation for how a first person camera would rotate. The yaw for a first person camera is unaffected by the previous pitch. Looking up and then rotating wouldn't make your vision go sideways.


I'm trying to implement rotation for how a first person camera would rotate. The yaw for a first person camera is unaffected by the previous pitch. Looking up and then rotating wouldn't make your vision go sideways.

Still need a better idea what you're trying to do. Can you describe how you want the camera to behave, rather than how you don't want it to behave? Are you perhaps trying to use yaw, pitch and roll for a view matrix, trying to keep the up direction along the Y axis?

If so, you may want to try a "look-at" matrix rather than yaw/pitch/roll (forget about roll). That is, something like XMMatrixLookAtLH using an eye location, the position at which you want to look, and an up vector of (0,1,0). For "yaw," rotate the look-at point about the y-axis. For pitch, rotate the position about the camera's "direction-to-the-right" axis (which changes with Y axis rotation). Just keep using the Y axis as the up vector.

EDIT: Although I'm still not sure how you want the camera to behave, a suggestion to try out.

After you calculate the rotation matrix from the roll and pitch only, multiply the vector (0,1,0) by the inverse of the rotation matrix. Use the resulting vector as the axis for your yaw rotation.

Just a thought.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.


Currently, for each object I am storing a persistent matrix that represents all of the translations and rotations that have occurred since it was created. Each frame, this matrix is incrementally updated.

I believe this is where your problem lies. You cannot use the same matrix over and over again and just multiply it with another matrix (y-rotation matrix) to get what you want.

If you just re-use the same matrix and multiply it "to the right" with the new rotation matrix, your new rotation will always be affected by whatever rotation and translation there already is in the old matrix. If you multiply "to the left", your new matrix will affect all of your old translations and rotations. So there's no way to get your new matrix to act independently of the old, persistent one.

Instead, you should keep separate yaw, pitch and position variables for each object, and re-build the whole matrix for each object every time one of the yaw, pitch or position variables changes (or just before drawing your objects). To get a first-person-like camera, you have to multiply all the matrices obtained from the three variables in this order: Translation * RotationYaw * RotationPitch.

You could also use a lookAt transformation like others suggested, but I think that might be more computationally-expensive (rotating the lookAt point requires sine/cosine operations, which is more work than just incrementing/decrementing the yaw or pitch variables and delaying the sine/cosine rotations until draw-time).


I've tried this same approach without success, it simulates the first picture (just tried it again to make sure). It acts as if you only used the code I have posted :/

But from my understanding of what your problem is, Buckeye's solution (post #2) seems me correct (assuming you are dealing with row vectors).

Let's pick the upper-right corner which is located at (1,1,0). Rolling is a z-axis rotation. Rolling by 45° gives you the new location (0,1.41,0) for the corner of interest:

(1,1,0,1) * Rz(45°) = (0,1.41,0,1)

An axis of rotation is the set of all points in space that are mapped onto themselves. If you now pick y-axis as axis of rotation, and since (0,1.41,0,1) is part of the y-axis, it will be mapped onto self:

(0,1.41,0,1) * Ry(45°) = (0,1.41,0,1)

This is because of the specific distribution of zeros in both the input vector and Ry; looking at each co-ordinate in isolation gives you:

x' := x * Ry;xx + y * Ry;xy + z * Ry;xz + 1 * 0 = 0 * Ry;xx + y * 0 + 0 * Ry;xz = 0

y' := x * Ry;yx + y * Ry;yy + z * Ry;yz + 1 * 0 = 0 * 0 + y * 1 + 0 * 0 = y

z' := x * Ry;zx + y * Ry;zy + z * Ry;zz + 1 * 0 = 0 * Ry;zx + y * 0 + 0 * Ry;zz = 0

(This works for any arbitrary axis, but is more complicated to demonstrate.)
As you can see: (0,y,0,1) is ever mapped onto (0,y,0,1) with an y-axis rotation. And that is in effect the behavior you've marked with the check-sign, haven't you?

That means if you have an orientation and concatenate the y-axis rotation to the right (for row vectors), then you rotate around the global y-axis.

Notice that if you have a pitch in-between then the corner itself is no longer on the chosen axis of rotation, and hence the corner will wander on a perpendicular plane. But that makes no difference in principle, because any point on the y-axis will still be mapped onto self.


If you just re-use the same matrix and multiply it "to the right" with the new rotation matrix, your new rotation will always be affected by whatever rotation and translation there already is in the old matrix. If you multiply "to the left", your new matrix will affect all of your old translations and rotations. So there's no way to get your new matrix to act independently of the old, persistent one.

The new rotation (the one to the right if using row vectors) works the same regardless of what is on the left: It ever rotates around an axis in the effective space. Of course, the result will be different if you have different stuff on the left side, but the rotation is not affected by that. This is important because it allows to do the following:

One can ever compose a transform that does the wanted stuff regardless how many transforms has been done so far. When looking at how translations, rotations, and scaling work, one can see that

* translations can be concatenated anyway,

* rotations may be surrounded by translations to relocate the axis of rotation, and

* scalings may be surrounded by translations and rotations to justify the directions of scaling.

Example: W.r.t. my previous post above, the point (0,0,0,1) will ever be mapped onto self regardless of which rotation is applied. Hence every axis of rotation passes through (0,0,0). If you want to pick another "origin", say p, you make that point temporarily to be (0,0,0) and apply the wanted rotation in that space:

T(-p) * R * T(p)

Similarly, scaling works along the particular axes and with (0,0,0) as center. Hence arbitrary scaling around another center p and an orientation O needs a composed temporary correction like so:

T(-p) * O-1 * S * O * T(p)

With stuff like the above in mind, one can derive any transform wanted. It is true that for special cases the same result can be yield in by using a simpler way. However, also in that cases the above math can be used to derive the simpler way. For example, it can be used to show that "local" yaw/pitch/roll can be computed by using the reverse order roll/pitch/yaw.


It ever rotates around an primary axis in the effective space.

Fixed that for you. The distinction between "primary axis", "arbitrary axis" and "primary axis-aligned" is really important here. If your persistent matrix is the identity matrix (no rotation or translation), then the first rotation you apply to it (multiplying either to the left or to the right with a translation matrix) will be a rotation around a primary axis. If you already have translation in the persistent matrix, the rotation becomes rotation around an primary axis-aligned axis (so this rotation will not result in a "look-at" type of transformation). If you also have rotation (in addition to translation) in your initial persistent matrix, and you want to apply new rotation around an axis, independently of the translation or the other axes, then you'd have to somehow remove the rotation around the wanted axis from the persistent matrix (which I think involves at least a matrix inversion), change the rotation, and re-multiply the persistent matrix with the new rotation... this is way too much work than just keeping separate variables for position and rotation.

Anyway, is anything I said not true? I can't handle your math explanations right now, sorry... The reason I know that what I said IS true, is because I had the same problem described in the OP at one time, and it turns out I couldn't just keep a persistent matrix that I could infinitely multiply to the left or right to get yaw and pitch rotations to act independently of each other and independently of the translation (position).

The simple explanation for how matrix concatenation works is that the transformations are applied in the order in which their corresponding matrices are multiplied. Also, each transformation by itself is relative to the origin (translation) or the axes (rotation) of the coordinate system, but when you concatenate transformations together, the origin and the axes of the coordinate system are relative to the previous transformations. So a translation matrix will move the origin of future transformations, and a rotation matrix will rotate the axes of future transofrmations.

So if you have YawRotation * Translation * YawRotation in the persistent matrix, you will obviously not get what you want, because the second rotation will be affected by the previous rotation and translation (which results in a new origin and rotated X,Y,Z axes). Similarly, if you wanted to apply only two transformations (translation and yaw), then you could always multiply the yaw rotation matrix to the right of the persistent matrix, and the translation matrix to the left, and you would get correct results. However, if you also have pitch, which needs to be applied to the right as well, but AFTER the yaw rotation, then your pitch and yaw will end up affecting each other...

Note: My previous recommendation of using Translation * RotationYaw * RotationPitch works for a view matrix, but I think it should work the same for a world matrix, except the angles and position coordinates are negated... In the end, I think it's always best to figure out your own explanation for how matrix concatenation works. Different people will have different ways of explaining it (even though they're explaining the same thing :) ). For myself, I found it better to stick to the simple explanation above (third paragraph) than a mathematical one...

This topic is closed to new replies.

Advertisement