13

I'm writing a program that loads a file containing a scene description and then displays it using OpenGL. I'm using GLM for all of my math operations. Rotations in the scene file are stored in quaternion format. My scene management systems takes rotations for objects in the form of euler angles, and these angles are later converted to a rotation matrix when drawing.

My loading process thus takes the quaternion rotations, converts them to euler angles for storage in my object class, then converts these euler angles to rotation matrices for drawing. I'm using the glm::eulerAngles and glm::eulerAngleYXZ functions (respectively) to perform these two operations.

However, I am getting incorrect results. For example, if I understand correctly the quaternion {0.500 -0.500 0.500 0.500} (that's W X Y Z) should describe the rotation taking an arrow from the +Z axis to the +Y axis. When I run the program, however, I get the arrow pointing along the +X axis.

I would assume that there is some flaw in my understanding of the quaternions, but I am able to get my expected results by skipping the intermediary euler angle form. By converting the quaternion directly to a rotation matrix using glm::toMat4, I get a rotation that points my +Z arrow towards +Y.

I'm having trouble reconciling these two different outputs, considering that both methods seem both simple and correct. To simplify my question, why is it that these two seemingly equivalent methods produce different results:

glm::quat q(.5, -.5, .5, .5);
glm::vec3 euler = glm::eulerAngles(q) * 3.14159f / 180.f; // eulerAngleYXZ takes radians but eulerAngles returns degrees
glm::mat4 transform1 = glm::eulerAngleYXZ(euler.y, euler.x, euler.z);
// transform1 rotates a +Z arrow so that it points at +X

glm::quat q(.5, -.5, .5, .5);
glm::mat4 transform2 = glm::toMat4(q);
// transform2 rotates a +Z arrow so that it points at +Y
steve9164
  • 376
  • 2
  • 11
  • 21
iondune
  • 173
  • 1
  • 1
  • 7
  • In case you want your own function for experimenting and know whats behind the scenes I gave an answer [here](http://stackoverflow.com/a/35494709/4933053). – qrtLs Feb 24 '16 at 12:51
  • Aside: at least as of 0.9.9 `glm::eulerAngles(q)` returns its result in radians. See [`glm/gtc/quaternion.hpp`](https://glm.g-truc.net/0.9.9/api/a00663.html#gaf4dd967dead22dd932fc7460ceecb03f). – wardw Jul 31 '20 at 13:18

3 Answers3

7

You have probably figured this out by now... but

What eulerAngle sequence does the function:

glm::vec3 euler = glm::eulerAngles(q) * 3.14159f / 180.f;

return? If it does not return explicitly an 'YXZ' sequence, you will not be able to use the next function properly:

glm::mat4 transform1 = glm::eulerAngleYXZ(euler.y, euler.x, euler.z);

Your variable 'euler' must be the same sequence type as the function you specify to transform it into a rotation matrix.

After looking here it looks like the function 'glm::eulerAngles' returns 'XYZ' as pitch, yaw, and roll. Thus, assuming they are 'YXZ', or yaw, pitch, roll is incorrect.

As said before, with Euler angles and rotation matrices, order matters!

dinkelk
  • 2,766
  • 2
  • 24
  • 42
4

The order of multiplication is important when dealing with Euler angles. YXZ and XYZ produce very different rotations.

You could calculate separate matrices for each axis, and then multiply them together in the order your need.

glm::quat q(.5, -.5, .5, .5);
glm::vec3 euler = glm::eulerAngles(q) * 3.14159f / 180.f;

glm::mat4 transformX = glm::eulerAngleX(euler.x);
glm::mat4 transformY = glm::eulerAngleY(euler.y);
glm::mat4 transformZ = glm::eulerAngleZ(euler.z);

glm::mat4 transform1 =
    transformX * transformY * transformZ; // or some other order
Markus Jarderot
  • 79,575
  • 18
  • 131
  • 135
  • It seems that this will suffer from the exact same problems. The Euler angles are only valid for the order they're computed for. Putting them into matrices doesn't undo that fact. – JCooper Aug 21 '12 at 17:56
  • @Markus I considered that my conversion of euler angles to matrix could be the problem, but there is a discrepancy before that stage. For example, the quaternions (0.5, -0.5, 0.5, 0.5) and (0.707106, 0, 0.707106, 0) both convert to euler angles (0, 90, 0), despite representing two distinct rotations (+Z to +Y and +Z to +X respectively) when converted directly to a matrix. – iondune Aug 21 '12 at 23:06
1

I think the result is radian already, no need to convert.

glm::quat q(.5, -.5, .5, .5);
glm::vec3 euler = glm::eulerAngles(q); // * 3.14159f / 180.f;

glm::mat4 transformX = glm::eulerAngleX(euler.x);
glm::mat4 transformY = glm::eulerAngleY(euler.y);
glm::mat4 transformZ = glm::eulerAngleZ(euler.z);

glm::mat4 transform1 =
    transformX * transformY * transformZ; // or some other order
lbsweek
  • 4,230
  • 37
  • 37
  • Yes, noticed this too, and at least as of 0.9.9 is stated as returning radians in [`glm/gtc/quaternion.hpp`](https://glm.g-truc.net/0.9.9/api/a00663.html#gaf4dd967dead22dd932fc7460ceecb03f). I assume GLM changed this at some point since the original question in 2012. (Bet that was fun for some). – wardw Jul 31 '20 at 13:16