【问题标题】:glm::rotate() changes the rotation axis, but why?glm::rotate() 改变旋转轴,但为什么呢?
【发布时间】:2021-12-29 05:09:44
【问题描述】:

简单的测试代码

glm::mat4 m = glm::rotate(glm::mat4(1.0f), 0.78f, glm::vec3(0,1,0));

while (true) {
    glm::vec3 axis = glm::normalize(glm::vec3(m[0]));  // right vector
    PRINT_VEC("{0:.3f} {1:.3f} {2:.3f}", axis.x, axis.y, axis.z);
    m = glm::rotate(m, glm::radians(5.f), axis);  // 5 degrees each iteration
}

所以,假设我有一个模型矩阵从恒等式绕 y 轴旋转 0.78 弧度,然后我将围绕局部右向量旋转每一帧,这是矩阵的第一列(假设右手系统)。由于右向量是我旋转的轴,我希望它是恒定的,但事实并非如此。我不明白为什么glm::rotate 也会改变旋转轴。

输出变化很大,所以我不认为这是浮点精度错误。

0.657 -0.424 -0.623
0.643 -0.482 -0.595
0.626 -0.539 -0.563
0.608 -0.593 -0.527
0.588 -0.646 -0.487
0.566 -0.696 -0.442
0.543 -0.742 -0.393
0.518 -0.785 -0.339
0.491 -0.824 -0.281
0.464 -0.858 -0.219
0.435 -0.887 -0.153
0.406 -0.910 -0.084
0.377 -0.926 -0.012
0.347 -0.936 0.063
0.319 -0.937 0.140
0.292 -0.931 0.218
0.267 -0.917 0.296
0.244 -0.895 0.374
0.224 -0.864 0.450
0.208 -0.826 0.524

【问题讨论】:

  • 请注意标签。 glm(广义线性模型)!= glm-math(GLM - OpenGL 数学)。请注意,您可以单击标签以查看其内容。
  • @Rabbid76 哇,这是我没有注意到的细微差异,感谢您指出。

标签: c++ glm-math


【解决方案1】:

m[0] 并不是真正的“局部右向量”。 local 右向量是vec3(1,0,0),您应该使用它来实现所需的旋转:

glm::mat4 m = glm::rotate(glm::mat4(1.0f), 0.78f, glm::vec3(0,1,0));

while (true) {
    glm::vec3 axis = glm::normalize(glm::vec3(m[0]));  // right vector
    PRINT_VEC("{0:.3f} {1:.3f} {2:.3f}", axis.x, axis.y, axis.z);
    m = glm::rotate(m, glm::radians(5.f), glm::vec3(1,0,0));  // 5 degrees each iteration
}

打印:

0.711 0.000 -0.703
0.711 0.000 -0.703
0.711 0.000 -0.703
...

您的代码所做的是将矢量vec3(1,0,0) 转换为世界坐标系,然后围绕该数字矢量应用局部旋转。请注意,GLM 转换函数,如glm::rotate,应用右侧的转换;即

m = m * glm::rotate(glm::mat4(1.0f), glm::radians(5.f), axis);

因此,应用空间旋转(局部)最终与您在(世界)中计算矢量的空间不同。因此,当在局部坐标系中解释时,axis 只是一些任意向量。

或者,您可以使用世界矢量axis 在世界空间中围绕同一矢量旋转;你可以通过在左边相乘来做到这一点:

m = glm::rotate(glm::mat4(1.0f), glm::radians(5.f), axis) * m;

它给出的结果与上面的第一个版本相同。

编辑:使用正确与错误代码呈现的列向量:

【讨论】:

  • 就是这样!事实证明,在使用上/右/前向量时,我无法理解“局部空间”与“世界空间”的区别。感谢您的明确回答,我现在已经修复了我的代码中的几个错误。
【解决方案2】:

正如@Yakov 指出的那样,我的错误是我混淆了“本地空间”和“世界空间”。对于那些遇到同样问题的人来说,你需要的是两个空间之间的转换。总之,局部旋转应该在右边应用,而全局旋转应该在左边应用,我们应该明确指定乘法的顺序以防止错误。

if (local) {  // local space
    // using matrix
    glm::mat4 R = glm::rotate(radians, v); // rotation matrix4x4
    m = m * R;  // applied on the right

    // using quaternion
    glm::quat Q = glm::angleAxis(radians, v);  // rotation quaternion
    q = q * Q;  // applied on the right
}
else {  // world space
    // using matrix
    glm::mat4 R = glm::rotate(radians, v);     // rotation matrix4x4
    m = R * m;  // applied on the left

    // using quaternion
    glm::quat Q = glm::angleAxis(radians, v);  // rotation quaternion
    q = Q * q;  // applied on the left
}

要在世界空间和局部空间之间转换矢量(旋转轴),请执行以下操作:

world_v = m * local_v;  // using matrix
world_v = q * local_v;  // using quaternion

local_v = glm::inverse(m) * world_v;  // using matrix
local_v = glm::inverse(q) * world_v;  // using quaternion

例如,如果m[0] = (0.711, 0.000,-0.703) 是世界空间中的正确方向向量,那么它在本地空间中将是glm::inverse(m) * m[0] = (1,0,0),所以如果你想要围绕它进行局部旋转,你会这样做

m = m * glm::rotate(radians, glm::inverse(m) * m[0]);

如果 4x4 矩阵是正交的(没有非均匀缩放...),我们可以将 glm::inverse(m) 替换为 glm::tranpose(m),这给出了相同的结果但计算成本要低得多,o/w 我们无法避免逆运算。相比之下,计算四元数的逆非常便宜,因为glm::inverse(q) 只需要vec4 的点积,因此基于四元数的转换更好。

【讨论】:

    猜你喜欢
    • 2021-10-17
    • 1970-01-01
    • 2018-03-28
    • 2019-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-27
    相关资源
    最近更新 更多