Problem converting matrix to quaternion and back

I’m not sure exactly where to ask this, so I’ll try here.

I’ve put together a collada loader to load up an animated skeletal mesh. All of my keyframes load and display correctly. The collada keyframes use matrices. I’m trying to convert these matrices to quaternions to use slerp then convert them back to matrices to send them to the gpu. The problem is that I can’t even seem to convert a single matrix to a quaternion and back. Here’s an example:

``````
// set up example matrix
glm::mat4 m(0.0);
m[0][0] = 0.6143;    m[0][1] = 0.0000000001;    m[0][2] = 4.309;    m[0][3] = 0;
m[1][0] = -3.731;    m[1][1] = 2.176;           m[1][2] = .532;     m[1][3] = 0;
m[2][0] = -2.154;    m[2][1] = -3.769;          m[2][2] = .3071;    m[2][3] = 0;
m[3][0] = 4.61;      m[3][1] = 12.22;           m[3][2] = 8.013;    m[3][3] = 1;

PrintM(m); // just prints a glm matrix

// convert matrix to quat
glm::quat rotation = glm::quat_cast(m);
std::cout << rotation.x << ", " << rotation.y << ", " << rotation.z << ", " << rotation.w << std::endl;

// convert quat to matrix
m = glm::mat4_cast(rotation);
PrintM(m);  // just prints a glm matrix

//convert new matrix back to quat
glm::quat q = glm::quat_cast(m);
std::cout << q.x << ", " << q.y << ", " << q.z << ", " << q.w << std::endl;

``````

Here’s the output

0.6143 -3.731 -2.154 4.61
1e-010 2.176 -3.769 12.22
4.309 0.532 0.3071 8.013
0 0 0 1

1.06239, -1.59643, 0.921597, 1.0121

-5.796 -5.258 -1.273 0
-1.527 -2.956 -5.093 0
5.19 -0.792 -6.355 0
0 0 0 1

-1.06239, 1.59643, -0.921597, -1.0121

Notice the the two quaternions are the same except the signs are opposite. I am not a math expert so that’s my first problem. So, my question is, shouldn’t this process give me back the same matrix I used as input? I’m not worried about the translation portion of the matrix currently; I have that working. Any thoughts would be appreciated.

Thanks

[QUOTE=mgoetschius;1279226]Notice the the two quaternions are the same except the signs are opposite. I am not a math expert so that’s my first problem.
[/QUOTE]
In which case, they’re equal, i.e. both correspond to exactly the same rotation (in the same way that e.g. glRotate(angle,x,y,z) and glRotate(-angle,-x,-y,-z) do). They will both generate the same matrix, so there’s no way to get back the original “version” from the matrix. Nor should there be any need.

A quaternion can only represent an orthonormal matrix (one where all axes are mutually perpendicular and have unit length. That much should be obvious given that a quaternion only has 4 components (and a unit quaternion only has 3 degrees of freedom).

Your original matrix (rather, the top-left 3x3 submatrix, ignoring the translation and projection) clearly isn’t orthonormal (some of the elements are larger than one). It’s orthogonal, but with a scale factor of ~4.352. Similarly, the quaternion isn’t a unit quaternion. The resulting matrix isn’t even orthogonal (SVD gives a scale of [9.13, 9.13, 1]). I note that both matrices have roughly the same determinant (~83), but the uniform scale of the original has been distributed across two axes rather than three.

This suggests that the matrix-to-quaternion conversion assumes that the matrix is orthonormal and that the quaternion-matrix conversion assumes that the quaternion is a unit quaternion; if these conditions don’t hold, the result is likely to be garbage.

If you’re trying to represent arbitrary matrices, then a unit quaternion won’t suffice. You’d need to use SVD to decompose the matrix to two rotations and a scale, which in turn can be converted to two quaternions and a vector (i.e. you need 9 degrees of freedom to represent an arbitrary 3x3 matrix). If you only need to support orthonormal matrices, you should test with such.

Thank you for that thorough reply. It all makes sense to me. Now I’m off to bury my head into my linear algebra book to figure out exactly how to interpolate between these arbitrary matrices that the collada data is giving me.

Thanks again, I truly appreciate your time and effort.

[QUOTE=mgoetschius;1279231]I’ve put together a collada loader to load up an animated skeletal mesh. All of my keyframes load and display correctly. The collada keyframes use matrices. I’m trying to convert these matrices to quaternions to use slerp then convert them back to matrices to send them to the gpu.

… Now I’m off to bury my head into my linear algebra book to figure out exactly how to interpolate between these arbitrary matrices that the collada data is giving me.[/QUOTE]

Ok. Just keep in mind that interpolating matrices is complete nonsense. It’s the idea behind linear blend skinning (LBS), but while it is pretty cheap, it is the reason that you can end up with joint collapse and candy-wrapper effects as well.

A better solution is to upload the joint transforms in a quaternion-based form (e.g. as Dual Quaternions or Quaternion-Translation), interpolate those, and then use those to transform bind-pose mesh vertices in your shader.