旋转精度误差建立得太快?

时间:2018-12-05 21:54:32

标签: c++ opengl glm-math

一个接一个地旋转时,精度误差会累积。

但是我对错误累积的速度感到惊讶。

在此示例中,我将比较两个在理论上等效的转换。

在实践中,我只旋转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");
    }
}

2 个答案:

答案 0 :(得分:3)

如果只想继续沿同一轴旋转,那么最好只增加围绕该轴的旋转角度,并每次从该角度重新计算一个新矩阵。请注意,您可以直接compute a matrix for rotation around an arbitrary axis。例如,从欧拉角旋转建筑物通常既不是必需的,也不是一个很好的解决方案(奇异性,数值上不理想,行为不是很直观)。 glm::rotate()的重载占用一个轴和一个角度,您可以为此使用。

如果确实必须将围绕任意轴的许多任意旋转连接起来,那么使用四元数表示旋转可能会在数值上更稳定。由于您已经在使用GLM,因此可以在其中使用quaternions。您可能会发现this tutorial有用。

答案 1 :(得分:1)

浮点乘法并没有您想像的那么精确,并且每次将两个浮点数相乘都会失去精度,就像发现的那样。

通常,您不希望将转换存储为结果矩阵,而是存储为获取该矩阵所需的步骤;例如,如果仅执行单轴变换,则将变换存储为角度并每次都重新计算矩阵。但是,如果涉及多个轴,这将变得非常复杂。

另一种方法是使用本身可以精确转换的转换的基本表示形式。 Quaternions对此非常流行(每个Michael Kenzel's answer),但是另一种更易于可视化的方法是使用一对向量来表示变换,您可以重新构造归一化矩阵。例如,您可以将旋转视为一对向量forwardup。由此,您可以使用以下方法计算转换矩阵:

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向量分别为directionup并重新构造一个新的矩阵来对转换矩阵进行归一化。

与使用四元数相比,这确实需要更多的计算复杂性,但是我发现将头缠起来要容易得多。