This blog post will be a short one, but the problem I were facing took me some time to figure out since I have forgotton so much about the basic math involved and I never had a lot of experience using quaternions anyways.
Ever since I implemented a basic free fly camera there has been an issue with it’s movement. The camera didn’t behave correctly when moving and rotating the mouse calculating the new directions of the camera.
Example: I tilted the camera downwards but the camera moved upwards depending on how the camera were positioned.
I implemented transforms having three values a position, the scale and a quaternion representing the orientation of the transform. Having the orientation allows me to calculate the direction vectors like this:
Forward = Transform->Rotation * glm::vec3(0, 0, 1); Right = Transform->Rotation * glm::vec3(1, 0, 0); Up = Transform->Rotation * glm::vec3(0, 1, 0);
In our game world however the camera isn’t moved around, but the world is around the camera. Therefore we need to calculate the inverse of the orientation of the camera.
Quaternions that describe an orientation are defined to be always of unit length. Which gives us an advantage since we can only conjugate the orientation.
We have a quaternion q which is defined by [w, v]. Where w is the scalar value of the quaternion and v is a vector. In order to check if it’s a unit quaternion we can just check it’s magnitude.
Magnitude: M(q) = sqrt(w² + x² + y² + z²) Conjugate: q* = q(w, -v) Inverse: q-1 = q* / M(q)
An unit quaternion is given when M(q) = 1. This means that when the magnitude is 1 we can assume that q-1 = q*. There is another optimization we can do here and remove the sqrt in this case since sqrt(1) = 1.
So what I have done in the end is to conjugate the rotation when I calculate the view matrix of the camera:
InCamera->View = glm::mat4_cast(glm::conjugate(InTransform->Rotation)); InCamera->View = glm::translate(InCamera->View, -InTransform->Position);
Links about quaternions I found useful: