将mat4分解为平移和旋转——glm?

30

为了进行lerp,我需要将4x4矩阵分解成四元数和vec3。取四元数很简单,只需将矩阵传入构造函数即可,但我找不到获取平移的方法。肯定有办法吧?

6个回答

37

glm::vec3(m[3]) 是位置向量(假设 mglm::mat4


15
我认为值得解释一下_为什么_这个方法有效。翻译矩阵只是一个4x4的单位矩阵,其中第四列的位置上有一个“1”(第四行也是)。在GLM中,mat4是一个4个vec4的数组,每个vec4代表一列;由于数组是从零开始索引的,所以[3]可以获取到第四列。然后,glm::vec3(...)将其转换为一个vec3,丢弃第四个(未使用)部分,并给出了移动距离。 - anon

37

看起来glm 0.9.6支持矩阵分解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);

5
文档在这方面有点过时了(即使是对于当前的v0.9.7版本),为了让它工作,你需要包含<glm/gtx/matrix_decompose.hpp>而不是<glm/gtx/decomposition.hpp>。 - Eugene Kulabuhov
需要注意的是,如果你只想要翻译向量,这种方法计算效率极低。@kerim在下面的回答中会更快。 - PeteBlackerThe3rd

23

在版本glm-0.9.8.1中,您需要包含以下内容:

#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);

请记住,得到的四元数不正确。

它返回的是它的共轭!

要解决这个问题,请将以下代码添加到您的代码中:

rotation=glm::conjugate(rotation);


3
谢谢,这个API确实很奇怪。 - kungfooman
1
你是怎么知道返回的四元数是共轭的?文档中没有提到。 - tuket
1
您可以通过组合自己选择的转换矩阵并执行分解来进行测试以确认值。附:四元数的共轭表示其逆旋转。 - Konstantinos Roditakis

11

我想我会发布一个2019年的更新和完整答案。应该给予信用,这是基于valmo的答案,包括Konstantinos Roditakis的一些项目以及我遇到的一些额外信息。

无论如何,在版本0.9.9中,您仍然可以使用实验性矩阵分解:https://glm.g-truc.net/0.9.9/api/a00518.html

首先,我要添加的部分是因为我没有在其他地方看到它,除非您在下面的include之前定义以下内容,否则将出现错误:

#define GLM_ENABLE_EXPERIMENTAL

接下来,你必须包含以下内容:
#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);

此外,正如Konstantinos Roditakis的回答所述,四元数确实不正确,并且可以通过应用以下修复:
rotation = glm::conjugate(rotation);

7
我开发了自己的分解函数,它不需要“skew”和“perspective”组件。
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);
}

如果您不需要扩展性,那么它可以被进一步简化:
void decomposeMtx(const glm::mat4& m, glm::vec3& pos, glm::quat& rot)
{
    pos = m[3];
    rot = glm::quat_cast(m);
}

1
嘿...这个好像被低估了...看起来运行良好。谢谢。 我使用的glm版本似乎会产生错误结果-错误的,有一些e-16样式的东西,尽管只输入了一个干净的缩放矩阵和单一的平移...我认为分解可能存在一个bug或者可能没有对矩阵做出某些假设,这可能导致它的这个版本更加稳定。如果有人知道为什么这个分解似乎会产生不稳定的结果,而这个版本似乎工作得更好,我将不胜感激。 - Bradford Medeiros

3
抱歉迟到了。其实,你必须对结果四元数进行共轭,原因是在计算四元数的x、y、z分量时矩阵分量的减法顺序错误。 这里提供了解释和示例代码。
因此,在glm的decompose()方法中,matrix_decompose.inl文件中:
我们有:
    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);

应该是:

    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);

同时查看此处的实现非常接近于GLM中发现的那个,但是这是正确的实现。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接