17
\$\begingroup\$

I am using an FPS-like camera, and it uses quaternions. But, whenever I try looking up and then sideways, it tilts, and sometimes it can turn upside down. How can I fix this?

\$\endgroup\$
  • 3
    \$\begingroup\$ (Will Self-answer in 7 hours)... \$\endgroup\$ – Aeodyn Jun 15 '12 at 4:59
16
\$\begingroup\$

You could decompose your quaternion into a yaw/pitch/roll set of angles, but that's overkill usually.

Instead of composing your quaternions like this:

cameraOrientation = cameraOrientation * framePitch * frameYaw;

Try this:

cameraOrientation = framePitch * cameraOrientation * frameYaw;

It will then never generate tilt/roll and is equivalent to storing yaw and pitch separately

\$\endgroup\$
  • 3
    \$\begingroup\$ This. This is exactly what I was looking for. Solved my problem in a one-liner. Nice! \$\endgroup\$ – aardvarkk Apr 18 '13 at 4:25
  • 1
    \$\begingroup\$ Are framePitch and frameYaw float types? Also, I would be grateful for some clarification on your first sentence. \$\endgroup\$ – sirdank Oct 26 '15 at 20:00
  • 1
    \$\begingroup\$ @sirdank cameraOrientation, framePitch, and frameYaw are all quaternions (each quaternion is 4 floats or doubles). \$\endgroup\$ – Dan Dec 6 '16 at 23:16
8
\$\begingroup\$

This is a problem I had for a while, and I couldn't find any answers for, so I thought I would post it here.

It is actually quite simple. How you are most likely doing the rotations is like this:

currentDirection * newRotation;

But, doing it like this doesn't work either.

newRotation * currentDirection;

What you have to do, is do it in the first order for the up and down rotations, and in the second order for the sideways rotations.

For me, it was like this:

        if (keyboard.IsKeyDown(Keys.Up))
            Direction = Direction * Quaternion.CreateFromAxisAngle(new Vector3(1, 0, 0), TurnSpeed);
        if (keyboard.IsKeyDown(Keys.Down))
            Direction = Direction * Quaternion.CreateFromAxisAngle(new Vector3(-1, 0, 0), TurnSpeed);
        if (keyboard.IsKeyDown(Keys.Left))
            Direction = Quaternion.CreateFromAxisAngle(new Vector3(0, 0, 1), TurnSpeed) * Direction;
        if (keyboard.IsKeyDown(Keys.Right))
            Direction = Quaternion.CreateFromAxisAngle(new Vector3(0, 0, -1), TurnSpeed) * Direction;

For a reason why, the first way has the rotation relative to the current sideways direction, which you want for up and down, but you don't want that for the sideways rotations, which is why the second order is needed.

\$\endgroup\$
  • \$\begingroup\$ I had to wait 7 hours to answer my own... \$\endgroup\$ – Aeodyn Jun 15 '12 at 17:00
  • \$\begingroup\$ sorry, can you explain this with some screenshots? "first order" and "second order" is a bit confusing - thanks! \$\endgroup\$ – Atav32 Jun 16 '12 at 3:26
  • \$\begingroup\$ When working with Quaternions, order does matter, as it always does with 3D rotations. In my example, "Direction *= newRotation" is the same as "Direction = Direction * newRotation". But, with left and right rotations, we want it the other way around. This makes it not dependent on the up and down tilt, which causes roll. \$\endgroup\$ – Aeodyn Jun 16 '12 at 18:37
  • \$\begingroup\$ Oh I though lt the point of quarternions was that it avoided gimbal lock and the rotations were all independent, but I may have misread the textbook. So just to clarify, are you saying that if you reverse the order of the rotation transformations, you can prevent the camera from tilting? Or are manually setting the roll and pitch transformations to 0 when you rotate yaw? \$\endgroup\$ – Atav32 Jun 16 '12 at 19:45
5
\$\begingroup\$

For an FPS camera you usually don't want roll and are limited to +/- 90 degrees pitch, so I'd just keep track of the current state using yaw and pitch angles. The full power of quaternions isn't really helpful for this.

You can still convert the yaw/pitch angles to and from quaternions in case you want to transition between the FPS camera and animated cameras using quaternion keyframe interpolation, or something like that.

\$\endgroup\$
0
\$\begingroup\$

Another simple trick is to put the camera in a GameObject, and have the Yaw rotation control the game object, while the child camera is configured with the Pitch coordinates:

playerCameraHolder.transform.Rotate(0, rotationYaw, 0);
playerCamera.transform.Rotate(rotationPitch, 0, 0);
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.