Search code examples
openglgame-enginequaternionsglm-math

GLM quaternion reversed yaw


I am building a toy 3D engine using OpenGL and GLM. My axis system is right-handed, with X to the right, Y up and Z back.

An entity's transform is made up of a 3D vector and one of GLM's quaternions. The physics simluation is in 2D, so an entity's velocity is a 2D vector (there is no velocity on Y, only on XZ, the ground plane). Similarly, the angular velocity is only around the Y axis (yaw).

// Properly integrate the change in velocities
auto deltaPos = (physics->velocity + physics->lastVelocity) * 0.5f * deltaTime;
auto deltaYaw = (physics->yawVelocity + physics->lastYawVelocity) * 0.5f * deltaTime;
// Apply the velocities
transform->position += glm::vec3 {deltaPos.x, 0, deltaPos.y} * transform->orientation
transform->orientation = glm::quat(glm::vec3 {0, deltaYaw, 0}) * transform->orientation;

I'm rendering the world with a top-down camera, converting the orientation to a 4x4 matrix, multiplying it (in the right order) with a scale and translation matrix:

glm::mat4x4 transformMat =
        glm::translate(transform->position) *
        glm::mat4x4(transform->orientation) *
        glm::scale(glm::vec3 {size.x, 1.0f, size.y});

.. and sending it to the shader, where it's multiplied with the point coordinates and the projection and view matrices:

// projView is projection * view
gl_Position = projView * transform * vec4(vertPosition.xyz, 1.0);

However, while this code seems straightforward, the yaw of the objects seems to be sometimes flipped. When an object turns left, it starts moving left in the world (the position += deltaPosition * orientation line works properly), but it is rendered as rotating to the right, but moving to the left. This is an example image:

yaw problem example

It seems like quaternion's representation, or at least its quaternion - matrix conversion, negates the yaw "component", of the rotation, acting like Y is down, instead of up. However, when a vector is rotated by that quaternion, it seems to act properly, like Y is up. Interestingly, another part of the code (dealing with the vehicle's turns) seems to work properly only when the quaternion is rotated by -yaw instead of yaw. (The rendering issue persists).

Any explanations or means to fix this problem?


Solution

  • Turns out I was applying the quaternions wrongly: On this line:

    transform->position += glm::vec3 {deltaPos.x, 0, deltaPos.y} * transform->orientation
    

    the multiplication's definition in GLM was actually rotating the vector by the inverse of the quaternion. The correct line was:

    transform->position += transform->orientation * glm::vec3 {deltaPos.x, 0, deltaPos.y};
    

    Thank you to Nico Schertler for directing my attention in the right direction.