30

For purposes of lerping I need to decompose a 4x4 matrix into a quaternion and a vec3. Grabbing the quaternion is simple, as you can just pass the matrix into the constructor, but I can't find a way to grab the translation. Surely there must be a way?

Silverlan
  • 2,783
  • 3
  • 31
  • 66

6 Answers6

37

glm::vec3(m[3]) is the position vector(assuming m is glm::mat4)

kerim
  • 2,412
  • 18
  • 16
  • 14
    I think it's worth explaining _why_ this works. A translation matrix is just a 4x4 identity matrix with the positions in the fourth column (with a `1` in the fourth row of that). In GLM, a `mat4` is a 4-array of `vec4`, where each `vec4` represents one column; arrays are zero-indexed, so `[3]` gets the fourth column. Then `glm::vec3(...)` converts it to a vec3, discarding the fourth (unused) part, and giving you just the translation distance. – Nic Apr 17 '18 at 21:19
37

It looks like glm 0.9.6 supports matrix decomposition http://glm.g-truc.net/0.9.6/api/a00204.html

#include <glm/gtx/matrix_decompose.hpp>

glm::mat4 transformation; // your transformation matrix.
glm::vec3 scale;
glm::quat rotation;
glm::vec3 translation;
glm::vec3 skew;
glm::vec4 perspective;
glm::decompose(transformation, scale, rotation, translation, skew, perspective);
Short
  • 7,767
  • 2
  • 25
  • 33
valmo
  • 1,156
  • 2
  • 12
  • 22
  • 5
    The documentation is a bit outdated on that one (even for current v0.9.7), you need to include instead of for it to work. – Eugene Kulabuhov Jul 31 '16 at 21:26
  • It should be noted that if all you want is the translation vector, this approach is incredibly computationally inefficient. @kerim's answer below will be much faster. – PeteBlackerThe3rd Jun 03 '21 at 14:50
23

At version glm-0.9.8.1 you have to include:

#include <glm/gtx/matrix_decompose.hpp>

To use it:

glm::mat4 transformation; // your transformation matrix.
glm::vec3 scale;
glm::quat rotation;
glm::vec3 translation;
glm::vec3 skew;
glm::vec4 perspective;
glm::decompose(transformation, scale, rotation, translation, skew,perspective);

Keep in mind that the resulting quaternion in not correct. It returns its conjugate!

To fix this add this to your code:

rotation=glm::conjugate(rotation);

11

I figured I'd post an updated and complete answer for 2019. Credit where it's due, this is based off valmo's answer, includes some items from Konstantinos Roditakis's answer as well as some additional info I ran into.

Anyway, as of version 0.9.9 you can still use the experimental matrix decomposition: https://glm.g-truc.net/0.9.9/api/a00518.html

First, and the part I am adding because I don't see it anywhere else, is that you will get an error unless you define the following before the include below:

#define GLM_ENABLE_EXPERIMENTAL

Next, you have to include:

#include <glm/gtx/matrix_decompose.hpp>

Finally, an example of use:

glm::mat4 transformation; // your transformation matrix.
glm::vec3 scale;
glm::quat rotation;
glm::vec3 translation;
glm::vec3 skew;
glm::vec4 perspective;
glm::decompose(transformation, scale, rotation, translation, skew,perspective);

Also, the Quaternion, as stated in Konstantinos Roditakis's answer, is indeed incorrect and can be fixed by applying the following:

rotation = glm::conjugate(rotation);
Travis Vroman
  • 364
  • 2
  • 9
6

I made my own decompose function that doesn't need "skew" and "perspective" components.

void decomposeMtx(const glm::mat4& m, glm::vec3& pos, glm::quat& rot, glm::vec3& scale)
{
    pos = m[3];
    for(int i = 0; i < 3; i++)
        scale[i] = glm::length(vec3(m[i]));
    const glm::mat3 rotMtx(
        glm::vec3(m[0]) / scale[0],
        glm::vec3(m[1]) / scale[1],
        glm::vec3(m[2]) / scale[2]);
    rot = glm::quat_cast(rotMtx);
}

If you don't need scale either, it can be further simplified:

void decomposeMtx(const glm::mat4& m, glm::vec3& pos, glm::quat& rot)
{
    pos = m[3];
    rot = glm::quat_cast(m);
}
tuket
  • 3,232
  • 1
  • 26
  • 41
  • 1
    Hey...this is underrataed ... seems to work well. Thank you. The version of glm i'm on seems to give bad results - wrong, with some e-16 style stuff, despite inputting just a clean scaling matrix on the diagnol + single translation... I think decompose has a bug or maybe doesn't make certain assumptions about the matrix here maybe that end up making this version of it more stable. I'd appreciate an explanation of why this decompose seems to produce unstable results if anyone seems to know yet this one seems to work better? – Bradford Medeiros May 03 '23 at 05:56
3

Sorry for being late. Actually the reason you have to conjugate the result quat is wrong substraction order of matrix components when calculating x,y,z components of the quaternion.

Here is an explanation and sample code of how it should be.

So basically in glm, decompose() method, matrix_decompose.inl file:

We have :

    orientation.x = root * (Row[1].z - Row[2].y);
    orientation.y = root * (Row[2].x - Row[0].z);
    orientation.z = root * (Row[0].y - Row[1].x);

When it should be:

    orientation.x = root * (Row[2].y - Row[1].z);
    orientation.y = root * (Row[0].z - Row[2].x);
    orientation.z = root * (Row[1].x - Row[0].y);

Also see this impl which looks very close to the one found in GLM,but which is correct one.

Michael IV
  • 11,016
  • 12
  • 92
  • 223