2
\$\begingroup\$

I read in many sources about using quaternion to avoid gimbal lock but I can't apply this practically in my code, so I have a camera class I want to rotate it with mouse so I have Euler angles Pitch and Yaw, I have a camQuat qauaternion to store the rotation quaternion each frame, so I have the initCamera method which initialize the camera:

void Camera::initCamera(glm::vec3& pos, glm::vec3& center, GLfloat yaw, GLfloat pitch) {
    view = glm::translate(view, center - pos);
    camQuat = glm::quat(glm::vec3(glm::radians(pitch), glm::radians(yaw), 0.0));
    camQuat = glm::normalize(camQuat);
    glm::mat4 rot = glm::mat4_cast(camQuat);
    view = view * rot;
    view = glm::translate(view, pos - center);
}

then each frame I update the camera by this method

void Camera::rotate(GLfloat xoffset, GLfloat yoffset, glm::vec3& c) {
    xoffset *= this->mouseSensitivity;
    yoffset *= this->mouseSensitivity;
    view = glm::translate(view, c);
    glm::quat q(glm::vec3(glm::radians(yoffset), glm::radians(xoffset), 0.0f));
    q = glm::normalize(q);
    glm::quat temp(q);
    q = q * camQuat * glm::conjugate(q);
    camQuat = temp;
    glm::mat4 rot = glm::mat4_cast(q);
    view = view * rot;
    view = glm::translate(view, -c);
}

this makes the rotation but with gimbal look, what is the bug in my code or what is the right way to use quaternion to prevent the gimbal look?

Edit: the first image is the normal camera FPS the rotation should be around the x and y axis only (No Rolling), once I rotate around any axis (as I read until the angle becomes 90) the gimbal lock appear and the camera begins to roll around z like the second image. enter image description here

Normal camera gimbal lock

Solution: due to DMGregory link below this code works fine:

void Camera::rotate(GLfloat xoffset, GLfloat yoffset, glm::vec3& c, GLboolean constrainpitch) {
    xoffset *= this->mouseSensitivity;
    yoffset *= this->mouseSensitivity;
    glm::quat Qx(glm::angleAxis(glm::radians(yoffset), glm::vec3(1.0f, 0.0f, 0.0f)));
    glm::quat Qy(glm::angleAxis(glm::radians(xoffset), glm::vec3(0.0f, 1.0f, 0.0f)));
    glm::mat4 rotX = glm::mat4_cast(Qx);
    glm::mat4 rotY = glm::mat4_cast(Qy);
    view = glm::translate(view, c);
    view = rotX * view;
    view = view * rotY;
    view = glm::translate(view, -c);
}

I get the quaternion for both x and y then post multiply the QX by the view matrix then pre multiply the QY by the view matrix, the idea is rotating pitch globally and yaw locally to avoid unwanted creeping roll.

\$\endgroup\$

marked as duplicate by DMGregory, Community Jul 10 '17 at 14:25

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • \$\begingroup\$ Can you describe the gimbal lock symptoms you're observing? A typical scenario is that when you look straight up/down, attempting to turn left/right (eg sweeping my gaze from my right toe to right hand to right shoulder without lifting my head) instead spins the view, like you were standing on a turntable. I'm not fluent in GL, but it doesn't look like your code would have this symptom. There's another common rotation issue where a twist or roll "creeps in" without the sharp spin singularity of gimbal lock. Is it possible you're seeing that? \$\endgroup\$ – DMGregory Jul 10 '17 at 11:08
  • \$\begingroup\$ Check my answer here: How to avoid gimbal lock , its more less a duplicate of your question. \$\endgroup\$ – Maik Semder Jul 10 '17 at 11:21
  • \$\begingroup\$ I don't know if it fixes your issue but please avoid changing your camera by offset quaternions if you use yaw and pitch values. Instead of computing offset quats and adding them to current orientation - add offsets to yaw/pitch values and run 'initCamera' code every frame. \$\endgroup\$ – kolenda Jul 10 '17 at 11:27
  • \$\begingroup\$ @DMGregory I added the description of what happen \$\endgroup\$ – Mohamed Jul 10 '17 at 11:32
  • \$\begingroup\$ @MaikSemder I already read your answer before asking and as u answer I store the quaternion and post multiply it by the new quaternion calculated from Euler angles but still have that behavior. \$\endgroup\$ – Mohamed Jul 10 '17 at 11:34