一个接一个地旋转时,精度误差会累积。
但是我对错误累积的速度感到惊讶。
在此示例中,我将比较两个在理论上等效的转换。
在实践中,我只旋转2次而不是旋转1次,即可得到0.02度的误差。
我期望误差会更低。
是否有办法使这两个转换的结果更接近?除了使用双精度变量。
#include <glm/gtx/rotate_vector.hpp>
double RadToDeg(double rad)
{
return rad * 180.0 / M_PI;
}
const glm::vec3 UP(0, 0, 1);
void CompareRotations()
{
glm::vec3 v0 = UP;
glm::vec3 v1 = glm::normalize((glm::vec3(0.0491, 0.0057, 0.9987)));
glm::vec3 v2 = glm::normalize((glm::vec3(0.0493, 0.0057, 0.9987)));
glm::vec3 axis_0_to_1 = glm::cross(v0, v1);
glm::vec3 axis_1_to_2 = glm::cross(v1, v2);
glm::vec3 axis_global = glm::cross(v0, v2);
float angle_0_to_1 = RadToDeg(acos(glm::dot(v0, v1)));
float angle_1_to_2 = RadToDeg(acos(glm::dot(v1, v2)));
float angle_global = RadToDeg(acos(glm::dot(v0, v2)));
glm::vec3 v_step = UP;
v_step = glm::rotate(v_step, angle_0_to_1, axis_0_to_1);
v_step = glm::rotate(v_step, angle_1_to_2, axis_1_to_2);
glm::vec3 v_glob = UP;
v_glob = glm::rotate(v_glob, angle_global, axis_global);
float angle = RadToDeg(acos(glm::dot(v_step, v_glob)));
if (angle > 0.01)
{
printf("error");
}
}
答案 0 :(得分:3)
如果只想继续沿同一轴旋转,那么最好只增加围绕该轴的旋转角度,并每次从该角度重新计算一个新矩阵。请注意,您可以直接compute a matrix for rotation around an arbitrary axis。例如,从欧拉角旋转建筑物通常既不是必需的,也不是一个很好的解决方案(奇异性,数值上不理想,行为不是很直观)。 glm::rotate()
的重载占用一个轴和一个角度,您可以为此使用。
如果确实必须将围绕任意轴的许多任意旋转连接起来,那么使用四元数表示旋转可能会在数值上更稳定。由于您已经在使用GLM,因此可以在其中使用quaternions。您可能会发现this tutorial有用。
答案 1 :(得分:1)
浮点乘法并没有您想像的那么精确,并且每次将两个浮点数相乘都会失去精度,就像发现的那样。
通常,您不希望将转换存储为结果矩阵,而是存储为获取该矩阵所需的步骤;例如,如果仅执行单轴变换,则将变换存储为角度并每次都重新计算矩阵。但是,如果涉及多个轴,这将变得非常复杂。
另一种方法是使用本身可以精确转换的转换的基本表示形式。 Quaternions对此非常流行(每个Michael Kenzel's answer),但是另一种更易于可视化的方法是使用一对向量来表示变换,您可以重新构造归一化矩阵。例如,您可以将旋转视为一对向量forward
和up
。由此,您可以使用以下方法计算转换矩阵:
z_axis = normalize(forward);
x_axis = normalize(cross(up, forward));
y_axis = normalize(cross(forward, x_axis));
,然后从这些向量构建变换矩阵;给定这些轴和位置的pos
,(主要列)OpenGL矩阵将为:
{ x_axis.x, x_axis.y, x_axis.z, 0,
y_axis.x, y_axis.y, y_axis.z, 0,
z_axis.x, z_axis.y, z_axis.z, 0,
pos.x, pos.y, pos.z, 1 }
类似地,您可以通过从矩阵中分别提取Z和Y向量分别为direction
和up
并重新构造一个新的矩阵来对转换矩阵进行归一化。
与使用四元数相比,这确实需要更多的计算复杂性,但是我发现将头缠起来要容易得多。