5
\$\begingroup\$

I have a camera class in my code which stores two 4x4 matrices. One for the view matrix and one for the projection matrix. Originally, I had two floats in the class for pitch and yaw and these were updated based on keyboard and mouse input. Then on update I would do something like this to set the view matrix:

Matrix33 xRot = Matrix33::CreateRotationX(m_pitch);
Matrix33 yRot = Matrix33::CreateRotationY(m_yaw);

Matrix33 rot = xRot * yRot;

const Vector3& rUp = rot.GetColumn1();
const Vector3& rForward = rot.GetColumn2();

m_viewMatrix = Matrix44::CreateLookAt(m_position, m_position + rForward, rUp);

However, I'm now wanting to add roll to the equation but I know that if I carry on using this method I'm going to run into gimbal lock. I do have a function in my maths library to rotate about an arbitrary axis. The function definition is:

static Matrix33 CreateRotation(const Vector3 &rAxis, const float rad);

But I'm not sure, based on pitch/yaw/roll how to create the arbitrary axis to rotate around. This got me thinking that perhaps pitch/yaw/roll isn't the best way to be doing it so I thought I'd ask on here about what might be the best way? I don't even have to use CreateLookAt(), it just seemed easier at the time :)

I don't have a quaternion class yet but I could add one if that would be the best way. I think ideally I'd rather do it with just matrices for now, though.

Any help would be much appreciated.

\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

Your arbitrary axis for the static method of rotation around an arbitrary axis are as follows:

Vector3& pitchAxis = rot.GetColumn0();
Vector3& yawAxis = rot.GetColumn1();
Vector3& rollAxis = rot.GetColumn2();

Now, if on a partcular frame, you wanted to pitch 0.01 radians, yaw 0.25 radians, and roll 0.03 radians, you could do it like this:

Vector3& arbitraryAxis = (pitchAxis * 0.01f) + (yawAxis * 0.25) + (rollAxis * 0.03);
float& radiansToRotate = arbitraryAxis.Length();
arbitraryAxis /= radiansToRotate;

Matrix arbRotation& = CreateRotation(arbitraryAxis, radiansToRotate);
rot = arbRotation * rot;

Notice you no longer keep track of overall rotation angles of the camera any more (whew!), simply determine how much you want to YPR each frame and apply it.

\$\endgroup\$
7
  • \$\begingroup\$ Concerning re-orthonormalzing a matrix. I don't believe it is as big of an issue as some may think. I have run something like the above for over 15 hours at 60 times per second without seeing any noticeable distortion or skewing... It is there it just stays so small it isn't visibly noticeable... floating point error takes ages to add up to something significant, unless some operation mathematically compounds the error each time, then it explodes fast. The above example is safe from that. \$\endgroup\$
    – Steve H
    Commented Oct 7, 2011 at 17:53
  • \$\begingroup\$ This sounds about right for what I need, cheers. I'll give it a try when I get home later. Presumably using this method I wouldn't need to use CreateLookAt() because I'd just be multiplying the view matrix by the rotation matrix generated each frame? \$\endgroup\$
    – user10329
    Commented Oct 8, 2011 at 12:12
  • \$\begingroup\$ yes, that would be correct. You might want to initialize it with a createLookAt before the game loop starts just to place it where you want it. \$\endgroup\$
    – Steve H
    Commented Oct 8, 2011 at 12:39
  • \$\begingroup\$ Yep, this works brilliantly, thanks. What I actually ended up doing was adding a CreateRotation(float pitch, float yaw, float roll) function to my matrix class and in there I calculate the axes for each ie: Vector3 xAxis = Vector3::UNIT_VECTOR_X * pitch; then calculated the arbitrary axis and rads as you suggested and called my CreateRotate(arbAxis, rads) etc. \$\endgroup\$
    – user10329
    Commented Oct 9, 2011 at 14:14
  • \$\begingroup\$ @ Steve H re precision: it all depends on the matrices - you are right that for small, local-ish transforms rounding errors are essentially a non-issue. However, for more generic transforms which use large-ish values (e.g. > 100k decimal) it can be easy to introduce rounding problems from multiplying those large numbers by small values as part of rotation. It only takes some 50 muls by values near zero or one to utterly destroy an appropriately sized float's precision. + Well done on the answer, pretty much exactly the same as mine but worded better with a clear example. :) \$\endgroup\$
    – jheriko
    Commented Oct 10, 2011 at 17:17
3
\$\begingroup\$

One way around this problem to allow the view matrix to be kept from frame to frame instead of yaw, pitch and roll.

The idea behind doing this is that you can multiply the matrix by rotations around each of the three axes encoded in the 3x3 "rotation" part of the transform matrix to change yaw, pitch or roll incrementally. Because you continually apply small rotations using the ever changing local axes you will avoid gimbal lock - the camera won't suddenly flip upside down because the changes to the "up" vector part of the view matrix is always changing smoothly in small increments.

There are drawbacks however - you may need to orthonormalise the matrix to prevent distortion from the accumulation of rounding errors. At very low frame rates you might get unexpected results depending on the order in which you do the three rotations (e.g. if you get a single frame with such a big change in orientation that you still get gimbal lock).

\$\endgroup\$
3
  • \$\begingroup\$ So you mean alternate which axis I rotate each frame but only actually rotate one of them? That seems a bit messy. \$\endgroup\$
    – user10329
    Commented Oct 7, 2011 at 16:28
  • \$\begingroup\$ No. This is not what I mean at all. I mean rotate by all axes each frame - but the axes local to the camera instead of the world space axes. This is exactly the part of this which eliminates the gimbal lock problem described. \$\endgroup\$
    – jheriko
    Commented Oct 10, 2011 at 17:15
  • \$\begingroup\$ In fact the answer above describes exactly what I describe just much better, with clearer language and a concrete example. \$\endgroup\$
    – jheriko
    Commented Oct 10, 2011 at 17:17

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .