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
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.