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);
}