I am trying to convert the orientation of an OpenVR controller that I have stored as a glm::vec3 of Euler angles into a glm::fquat and back, but I get wildly different results and the in-game behavior is just wrong (hard to explain, but the orientation of the object behaves normally for a small range of angles, then flips in weird axes).

This is my conversion code:

// get `orientation` from OpenVR controller sensor data

const glm::vec3 eulerAnglesInDegrees{orientation[PITCH], orientation[YAW], orientation[ROLL]};

const glm::fquat quaternion{glm::radians(eulerAnglesInDegrees)};
const glm::vec3 result{glm::degrees(glm::eulerAngles(quaternion))};

// `result` should represent the same orientation as `eulerAnglesInDegrees`

I would expect eulerAnglesInDegrees and result to either be the same or equivalent representations of the same orientation, but that is apparently not the case. These are some example values I get printed out:

39.3851 5.17816 3.29104 
39.3851 5.17816 3.29104 

32.7636 144.849 44.3845 
-147.236 35.1512 -135.616 

39.3851 5.17816 3.29104 
39.3851 5.17816 3.29104 

32.0103 137.415 45.1592 
-147.99 42.5846 -134.841 

As you can see above, for some orientation ranges the conversion is correct, but for others it is completely different.

What am I doing wrong?

I've looked at existing questions and attempted a few things, including trying out every possible rotation order listed here, conjugating the quaternion, and other random things like flipping pitch/yaw/roll. Nothing gave me the expected result.

How can I convert euler angles to quaternions and back, representing the original orientation, using glm?

Some more examples of discrepancies:

original:      4; 175;   26; 
computed:   -175;   4; -153; 
difference:  179; 171;  179; 

original:     -6; 173;   32; 
computed:    173;   6; -147; 
difference: -179; 167;  179; 

original:      9; 268;  -46; 
computed:   -170; -88;  133; 
difference:  179; 356; -179; 

original:    -27; -73;  266; 
computed:    -27; -73;  -93; 
difference:    0;   0;  359; 

original:    -33; 111;  205; 
computed:    146;  68;   25; 
difference: -179;  43;  180; 

I tried to find a pattern to fix the final computed results, but it doesn't seem like there's one easy to identify.

GIF + video of the behavior:

Video excerpt

Visual representation of my intuition/current understanding:

Visual diagram

  • The above picture shows a sphere, and I'm in the center. When I aim the gun towards the green half of the sphere, the orientation is correct. When I aim the gun towards the red half of the sphere, it is incorrect - it seems like every axis is inverted, but I am not 100% sure that is the case.
  • \$\begingroup\$ What operation are you performing after the round-trip conversion that's revealing unwanted behavior? There's a decent chance the error is there, and the change of angle representation is just making it visible. \$\endgroup\$ – DMGregory Feb 22 at 3:08

The results are not what you expect, but they are not wrong. It’s just that for a given orientation there are at least two “paths” through Euler angles that lead there.

For instance, the identity quaternion is trivially converted to Euler angles [0,0,0]. But doing three 180-degree rotations around each axis leaves you in the same orientation, too. That means that [180,180,180] is a valid Euler angle representation of that quaternion, too.

Now since your character appears to be a human, it’s probable that their head can’t roll at 180-degree angles, so here is a simple method that you can use to fix your values:

if (std::fabs(result.z) >= 90) {
    result.x += 180.f;
    result.y = 180.f - result.y;
    result.z += 180.f;
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.