On GL camera orientation with quaternions
This is mostly a note to my future self, wanting to reimplement orientation of opengl camera with quaternions over and over again.
Problem can be put as follows: given camera position
, lookAt
and world up
vectors in world space, compute transform that takes opengl camera to
world. World uses right-handed coordinate system.
Camera orientation with quaternions allows for storing rotation and position components separately. It brings benefits such as spherical interpolation for camera animations.
To obtain camera matrix for conversion from world space to view
space in shaders, cameraMatrix
function can be used, that computes the
reverse transform in matrix form.
Code sample uses relies glm library which is too common to be ignored.
// Camera orientation in OpenGL space const glm::vec3 GLCameraForward = glm::vec3(0.0f, 0.0f, -1.0f); const glm::vec3 GLCameraUp = glm::vec3(0.0f, 1.0f, 0.0f); struct Transform { glm::vec3 translation; glm::quat rotation; } Transform cameraToLookAtTransform(const glm::vec3 &pos, const glm::vec3 &lookAt, const glm::vec3 &worldUp) { glm::vec3 direction = glm::normalize(lookAt - pos); glm::vec3 right = glm::normalize(glm::cross(direction, worldUp)); glm::vec3 desiredUp = glm::normalize(glm::cross(right, direction)); // Rotation from opengl camera direction to requested direction glm::quat glCamToWorld = glm::rotation(GLCameraForward, direction); // Up vector resulting from glCamToWorld rotation glm::vec3 newUp = glm::rotate(glCamToWorld, GLCameraUp); // Corrected rotation to bring newUp to desiredUp glm::quat correctedUp; // Special case when newUp and desiredUp vectors are in opposite // directions, so any rotation axis can be used, but problem is direction // might be changed, which is what we don't want. Axis that preserves // forward vector is ``direction'' itself. if (glm::dot(newUp, desiredUp) < -1.0f + std::numeric_limits<float>::epsilon()) { correctedUp = angleAxis(glm::pi<float>(), direction); } else { correctedUp = glm::normalize(glm::rotation(newUp, desiredUp)); } return Transform{pos, correctedUp * glCamToWorld}; } glm::mat4 cameraMatrix(Transform transform) const { // get quaternion corresponding to *reverse* of opengl -> world rotation // and build matrix from it glm::mat4 reverse = glm::mat4_cast(glm::conjugate(transform.rotation)); // build world -> opengl camera matrix by adding *reverse* of camera position return glm::translate(reverse, -transform.translation); }