I've made my own Camera object, with a forward vector as well as a up vector. I happen to be programming in Android using OpenGL ES 2.0 but I think this question is relevant to all frameworks, it seems to be some math issue I'm not understanding.

Here's a gif of my Pitch/Yaw working correctly:


Here's a gif of Pitch/Yaw in a circle, causing the square to rotate, because the camera is tilting (the up vector becomes negative as I continue).

Pitch/Yaw in a circle causes Tilting

Here's pseudocode for my pan/yaw functions:

function pitch(amount) {
    if (amount == 0) return;
    forward = normalize(forward + up * amount);

    // Now fix the incorrect up vector
    right = cross(forward, up);
    up = normalize(cross(right, forward));

function yaw(amount) {
    if (amount == 0) return;

    right = cross(forward, up);
    forward = normalize(forward + right * amount);

I get the same issue if I use Vector rotations instead of Vector Addition (amount is an angle and I use a Rotation Matrix in the X or Y axis)

Pitch: forward = normalize(rotated(forward, angle, Vector3(1, 0, 0)));
Yaw: forward = normalize(rotated(forward, angle, Vector3(0, 1, 0)));

Why is this happening? Is there a way to stop it?

EDIT: I followed the instructions at Why is the camera tilting around the z axis when I only specified x and y? to pitch using the local X axis and yaw using the world Y axis, same result.

Here's the new pseudocode:

function pitch(angle) {
    if (angle == 0) return;

    right = cross(forward, up);
    forward = normalized(rotated(forward, angle, right));

    // Fix the now incorrect up vector
    up = normalized(cross(right, forward));

function yaw(angle) {
    if (angle == 0) return;

    forward = normalized(rotated(forward, angle, Vector3(0, 1, 0)));

You can see all the code at https://github.com/repwolfe/paper-escaper-android/tree/master/app/src/main/java/com/studioussoftware/paperescaper

  • 4
    \$\begingroup\$ This question gets asked a lot. See: Why is the camera tilting around the z axis when I only specified x and y? and Unwanted roll when rotating camera with pitch and yaw \$\endgroup\$ – DMGregory Sep 4 '16 at 16:39
  • \$\begingroup\$ Didn't fix it, see edit \$\endgroup\$ – robev Sep 4 '16 at 17:18
  • \$\begingroup\$ Your code is allowing errors to accumulate in the up vector. Try replacing right = cross(forward, up) with right = normalized(cross(forward, Vector3(0, 1, 0))) if you don't need to look directly up/down. If you're using up to construct your matrix then you'll want to update it when yawing too. \$\endgroup\$ – DMGregory Sep 4 '16 at 17:34
  • \$\begingroup\$ That alone fixed everything, I didn't even need to use the local X axis when pitching. You should make it an answer. I'm going to revert back to what I had before, if I have weird rolling then I'll use the local X axis. Thanks! \$\endgroup\$ – robev Sep 4 '16 at 17:47
  • \$\begingroup\$ "I didn't even need to use the local X axis when pitching" - your previous code used the local x axis for pitch rotation too. The trouble was that you also used local y rotation without correcting for roll. You can combine local pitch (<90) and global yaw without problems, as long as you're careful not to allow rounding errors to accumulate. \$\endgroup\$ – DMGregory Sep 4 '16 at 18:44

You're using the previous value of the up vector to update the right vector and the up vector itself. That means any small deviation from the vertical will tend to accumulate in the up vector and snowball over repeated updates.

Instead, you can define right in reference to the global up vector (0, 1, 0) so this error can't accumulate:

right = normalized(cross(forward, Vector3(0, 1, 0)));
up = cross(forward, right);

Note that this is not suitable when forward points nearly parallel to the vertical axis. You can either clamp the camera's pitch to stay away from this trouble spot, or apply the up correction gradually over multiple frames to avoid a sudden pop or lurch.

| improve this answer | |

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