Quaternion reaching gimbal lock

c++, euler-angles, quaternions, rotation, transformation

Solution

Your issue is that even when you're using quaternions, you're still storing three pitch, yaw, and roll values, rather than a quaternion, to represent an object's orientation.

Here's how you should use quaternions for rotation here:

Instead of storing X, Y, Z, pitch, yaw, roll for each object, instead store X, Y, Z, `orientation` in each object, where `orientation` is a quaternion starting at the initial value (0, 0, 0, 1), meaning no rotation. Storing pitch, yaw, and roll for each object is vulnerable to singularities (gimbal lock) because as small changes are added, one of the intermediate rotations (say, a pitch) could result in the object being parallel to a rotation axis (say, the yaw axis), so that the next rotation about that axis could fail.

Then, as an object is rotated, determine the pitch, yaw, and roll for that object that occurred during that frame (assuming that your input device provides the rotation in that form), convert it to a quaternion, then pre-multiply that quaternion into the object's `orientation` quaternion. This approach is less vulnerable to singularities because the changes in the rotation are expected to be very small each frame.

Don't modify the object's X, Y, and Z (your `verticies` array) directly after changing the orientation. Instead, when an object's orientation changes, create a new rotation matrix to serve as part of the object's world transformation matrix (along with scaling and translation; for best results, calculate the world transform as `translation * rotation * scaling`).

Every few frames, you should normalize the `orientation` quaternion to avoid undesirable changes in the orientation, which can happen due to rounding error.

Problem

In attempt to avoid angle lock when performing rotations I've tried to switch over to Quaternions. Somehow, I'm still managing to reach gimbal lock. I'm not sure if its due to the math I've implemented, or a design error, so please point out if I should change my approach for my object coordinates. Each of my objects hold an X,Y,Z value, and a pitch,yaw,roll value. When I change a rotation value, the object recalculates its vertices based on the above information. This logic is as follows: ``` // vertex array vertices[x] -= /*Offset by origin point*/; // Quat.'s representing rotation around xyz axes Quaternion q1 = Quaternion(glm::vec3(1,0,0),pitch); Quaternion q2 = Quaternion(glm::vec3(0,1,0),yaw); Quaternion q3 = Quaternion(glm::vec3(0,0,1),roll); // total rotation Quaternion TotalRot = ( (q3 * q2) * (q1) ); // conversion of original coordinates to quaternion Quaternion Point1 = Quaternion(0, vertices[x].x(), vertices[x].y(), vertices[x].z()); // resulting rotated point Quaternion Point2 = Quaternion( (TotalRot * Point1) * TotalRot.inverse() ); // placing new point back into vertices array vertices[x] = QVector3D(round(Point2.v.x),round(Point2.v.y),round(Point2.v.z)); vertices[x]+= /*Undo origin point offset*/; ``` "vertices[]" is the objects vertex array. The origin point offset commented out above is just so that the object is rotated around the proper origin point, so it is shifted relative to 0,0,0 since rotations occur around that point (right?). I have a pictorial representation of my problem, where I first yaw by 90, pitch by 45, then roll by -90, but the roll axis became parallel to the pitch axis: Edit: I tried multiplying those 3 axis quaternions together, then multiplied by a 4x4 matrix, followed by multiplying that by my vertex point, but I still gimbal lock/reach singularity! ``` Quaternion q1 = (1,0,0,pitch); Quaternion q2 = (0,1,0,yaw); Quaternion q3 = (0,0,1,roll); Quaternion qtot = (q1*q2)*q3; Quaternion p1(0, vertices[x].x(), vertices[x].y(), vertices[x].z()); QMatrix4x4 m; m.rotate(qtot); QVector4D v = m*p1; vertices[x] = QVector3D(v.x(),v.y(),v.z()); ```

Original source