1
\$\begingroup\$

I am working on a camera class that will have full range of motion (pitch, yaw, and roll). When only altering pitch and yaw, I am getting a large amount of roll.

unwanted roll

I understand that the issue is related to: I'm rotating an object on two axes, so why does it keep twisting around the third axis?

However, I have not been able to come up with a solution. I would like the camera to have all motion (i.e. like a spaceship). Here is the relevant code:

void Camera3D::update(const glm::vec2 & current_mouse_coords){

    if (m_mouse_first_movement) {
        if (current_mouse_coords.x != 0 || current_mouse_coords.y != 0) {
            m_mouse_first_movement = false;
        }
    } else {
        const glm::vec2 mouse_delta = (current_mouse_coords - m_old_mouse_coords) * mouse_sensitivity;
        pitch(-mouse_delta.y);
        yaw(-mouse_delta.x);
    }   
    m_old_mouse_coords = current_mouse_coords;
}

void Camera3D::pitch(const float angle){

    // Pitch Rotation
    const glm::quat pitch_quaternion = glm::angleAxis(-angle, m_camera_right);

    // Update Vectors
    m_camera_up = glm::normalize(glm::rotate(pitch_quaternion, m_camera_up));
    m_camera_forward = glm::normalize(glm::rotate(pitch_quaternion, m_camera_forward)); 
}

void Camera3D::yaw(const float angle){

    // Yaw Rotation 
    const glm::quat yaw_quaternion = glm::angleAxis(angle, m_camera_up);

    // Update Vectors
    m_camera_right = glm::normalize(glm::rotate(yaw_quaternion, m_camera_right));
    m_camera_forward = glm::normalize(glm::rotate(yaw_quaternion, m_camera_forward));
}

glm::mat4 Camera3D::get_view_matrix(){
    m_view_matrix = glm::lookAt(
        m_camera_position,
        m_camera_position + m_camera_forward,
        m_camera_up
    );  
    return m_view_matrix;
}

I would like the movement of the camera to be based on local coordinates so the controls (up/down/left/right/vertical up/vertical down) move the camera along its own local axis.

Additional clarification based on the comment below: If I look up 60 degrees and then left, I would like the horizon to stay level (essentially adding roll in the opposite direction to keep the horizon level)

Any help is appreciated. Thank you!

\$\endgroup\$
  • 3
    \$\begingroup\$ "I would like the camera to have all motion (i.e. like a spaceship)" Then your camera already behaves correctly. Games like Space Engineers show the same behavior. \$\endgroup\$ – HolyBlackCat Jun 21 at 22:36
  • \$\begingroup\$ "As expected" needs some unpacking here. If I'm looking 60° up, and I look left, I "expect" my view to swivel directly left, relative to my current orientation. This adds roll. But I also "expect" the horizon to stay level if I haven't deliberately rolled. These two expectations are in conflict. We need to violate one or the other, or bend both to some extent. So we need more details from you as to what behaviour your ship should have in this situation — which expectations can we bend and which ones do we need to preserve, or in some way placate. \$\endgroup\$ – DMGregory Jun 21 at 22:40
  • \$\begingroup\$ I added some clarification above. I would like like the horizon to stay level unless roll is deliberately applied. \$\endgroup\$ – Pb1357 Jun 21 at 23:00
  • \$\begingroup\$ If that goal cannot be bent, then you must sacrifice this one: "I would like the movement of the camera to be based on local coordinates so the controls (up/down/left/right/vertical up/vertical down) move the camera along its own local axis". And you will experience gimbal lock when looking close to vertical. If that's OK, you can use the examples from the answer of mine that you've linked to accomplish that. If not, then you'll need to articulate a compromise between the two goals. \$\endgroup\$ – DMGregory Jun 21 at 23:06
  • 1
    \$\begingroup\$ Use Euler angles for camera control as Ocelot suggests. By the way, your requirement about the horizon loses meaning when the camera is looking straight up or straight down. Which is where you would expect a Gimbal lock with Euler angles. Which suggest that Euler angles is what you want… If you want to avoid Gimbal lock, you will have to relax the requirement of keeping the horizon horizontal. \$\endgroup\$ – Theraot Jun 22 at 1:29
3
\$\begingroup\$

It seems like you accumulate transformations. What you need to do instead is to keep rotation as euler angles pitch and yaw and then calculate final rotation from them.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ It's not compatible with spaceship motion, however. \$\endgroup\$ – user253751 Jun 23 at 20:20
  • \$\begingroup\$ @user253751 for a spaceship rotation OP has it already implemented, but he wanted the roll to be stabilized, so that's the answer. You can use another matrix/quaternion as up basis for Euler angles. \$\endgroup\$ – Ocelot Jun 24 at 0:42
0
\$\begingroup\$

Thanks for all answers. Here was my solution:

The camera is set to have 2 modes (for now). The two modes are:

  • Flight (Fixed Yaw Axis)
    • enter image description here
    • Regardless of where the camera is looking, yaw is rotated along the global y axis. This was based on the linked post that 'DMGregory' wrote. As you can see from the gif, this has the effect of 'stabilizing' the horizon. Pitch is clamped at +/- 85 degrees. Flight movement controls are based on local camera coordinates. For example, when I use vertical up movement, this uses the cameras local up axis, not the global y axis. In order to get this axis, I simply take the cross product of the camera_forward vector with the camera_right vector. I can also see how a user may want vertical up/down to be tied to the world up/down, and will probably make that an option (i.e. setting the vertical movement axis as the world_up).
  • Flight (Roll Enabled)
    • Roll Mode
    • For flight mode without a fixed yaw axis, everything is based on local camera coordinates. Yaw is being replaced by roll in this mode and is not used at all. Roll is done along the cameras forward vector. This gives a fairly natural feel for how you would control a spaceship. This also has the added benefit of not running into any gimbal lock, so there is full rotation along any axis. The movement vertical up/down controls are based on the local camera_up vector. As a side note, yaw could also be included in this mode (along the local cameras up vector). However, for my camera system I added a toggle between modes. When the user is in fixed yaw mode, left and right on the mouse result in yaw changes. When the user enters flight without fixed yaw, roll is used based on left and right mouse movement. Yaw could be added in and tied to a key if desired.
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Perhaps make it 90 degrees so the player can look directly up and down - depending on your game of course. \$\endgroup\$ – user253751 Jun 23 at 20:22

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.