Search code examples
c++mathrustglm-mathraytracing

How does GLM achieve quaternion crossing and normalization?


I'm trying to follow TheCherno's ray tracing series in Rust (Currently on Episode 6), but I've run into a mathematical roadblock. I'm uncertain what to do about this bit of code:

if (delta.x != 0.0f || delta.y != 0.0f)
    {
        float pitchDelta = delta.y * GetRotationSpeed();
        float yawDelta = delta.x * GetRotationSpeed();

        glm::quat q = glm::normalize(glm::cross(glm::angleAxis(-pitchDelta, rightDirection),
            glm::angleAxis(-yawDelta, glm::vec3(0.f, 1.0f, 0.0f))));
        m_ForwardDirection = glm::rotate(q, m_ForwardDirection);

        moved = true;
    }

How does GLM allow for crossing and normalizing quaternions? I'm new to the concept, but it doesn't seem mathematically sound. I'm trying to rewrite this code in Rust with the cgmath crate, but there is no counterpart to these two GLM functions. I've tried finding the source code and implementing the functions myself, but I can't find any mention apart from in the ancient documentation.


Solution

  • So anyway, this code is terrible, so don't feel too bad for not understanding it :)

    Ok, so what we have here is pretty poor usage of quats tbh. The code is generating two quaternions, one for the rotation around the x axis (I assume), and one around the y axis.

    auto qr_xaxis = glm::angleAxis(-pitchDelta, rightDirection);
    auto qr_yaxis = glm::angleAxis(-yawDelta, glm::vec3(0.f, 1.0f, 0.0f);
    

    cross in GLM is actually just multiply - naming it cross is very wrong indeed! Effectively all this is doing is combining the X and Y axis rotations into a single rotation:

    glm::quat q = qr_xaxis * qr_yaxis;
    

    For reasons unknown, the author has decided that normalising the result here is a good idea. There shouldn't be any need to normalise that quat here, because by definition qr_xaxis and qr_yaxis are already normalised, and the quat product of two unit quaternions is always a unit quaternion.

    Finally, its rotating m_ForwardDirection by the combined quat.

            m_ForwardDirection = glm::rotate(q, m_ForwardDirection);
    

    Honestly, I'm not entirely sure what this code is trying to do, but I will say that the original authors understanding of how quaternions work, is clearly not great. I'd treat his tutorial series with a large dose of salt as a result...