glm::rotate() 改变了旋转轴,但为什么呢?
glm::rotate() changes the rotation axis, but why?
简单的测试代码
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
m[0]
并不是真正的 'local right vector'。 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);
因此,应用 space 旋转(本地)最终不同于 space 您在(世界)中计算的矢量。因此,当在局部坐标系中解释时,axis
只是一些任意向量。
或者,您可以使用世界矢量 axis
在世界中围绕同一矢量旋转 space;你通过在左边乘法来做到这一点:
m = glm::rotate(glm::mat4(1.0f), glm::radians(5.f), axis) * m;
给出的结果与上面的第一个版本相同。
编辑: 使用正确代码与不正确代码呈现的列向量:
正如@Yakov 所指出的,我的错误是我混淆了“local space”和“world space”。对于遇到相同问题的那些人,您需要的是两个 space 之间的转换。综上所述,局部旋转应该在右边,而全局旋转应该在左边,我们应该明确指定乘法顺序以防止错误。
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
}
要在世界 space 和本地 space 之间转换矢量(旋转轴),请执行:
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)
是世界 space 中的正确方向向量,则在本地 space 中它将是 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
的点积,因此基于四元数的转换更好。
简单的测试代码
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
m[0]
并不是真正的 'local right vector'。 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);
因此,应用 space 旋转(本地)最终不同于 space 您在(世界)中计算的矢量。因此,当在局部坐标系中解释时,axis
只是一些任意向量。
或者,您可以使用世界矢量 axis
在世界中围绕同一矢量旋转 space;你通过在左边乘法来做到这一点:
m = glm::rotate(glm::mat4(1.0f), glm::radians(5.f), axis) * m;
给出的结果与上面的第一个版本相同。
编辑: 使用正确代码与不正确代码呈现的列向量:
正如@Yakov 所指出的,我的错误是我混淆了“local space”和“world space”。对于遇到相同问题的那些人,您需要的是两个 space 之间的转换。综上所述,局部旋转应该在右边,而全局旋转应该在左边,我们应该明确指定乘法顺序以防止错误。
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
}
要在世界 space 和本地 space 之间转换矢量(旋转轴),请执行:
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)
是世界 space 中的正确方向向量,则在本地 space 中它将是 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
的点积,因此基于四元数的转换更好。