Ошибка точности вращения нарастает слишком быстро? - PullRequest
0 голосов
/ 06 декабря 2018

При применении поворотов один за другим накапливаются погрешности точности.

Но я удивляюсь, как быстро нарастает ошибка.

В этом примере я сравниваю 2 преобразования, которые эквивалентны вТеория.

На практике я получаю ошибку 0,02 градуса, выполняя только 2 поворота вместо одного.

Я ожидал, что ошибка будет ниже.

Есть ли способприблизить результат этих двух преобразований?За исключением использования переменных двойной точности.

#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 голосов
/ 06 декабря 2018

Умножение с плавающей запятой не так точно, как вы думаете, и каждый раз, когда вы умножаете два числа с плавающей запятой, вы теряете точность - довольно быстро, как вы обнаружили.

Как правило, вы хотите сохранитьваши преобразования не как матрица результатов, а как шаги, необходимые для получения этой матрицы;Например, если вы выполняете только одноосное преобразование, вы сохраняете свое преобразование как угол и каждый раз пересчитываете матрицу.Однако, если задействовано несколько осей, это очень сложно очень быстро.

Другой подход заключается в использовании базового представления преобразования, которое само может быть преобразовано точно. Кватернионы очень популярны для этого (согласно ответу Майкла Кензеля ), но другой подход, который может быть проще визуализировать, - это использовать пару векторов, которые представляют преобразование таким образом, что выможно восстановить нормализованную матрицу.Например, вы можете думать о своем вращении как о паре векторов 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, соответственно, и восстановление из них новой матрицы.

Это требует гораздо большей вычислительной сложности, чем использование кватернионов, но я считаю, что гораздо проще обернуть голову.

0 голосов
/ 06 декабря 2018

Если вы просто хотите продолжить вращение вдоль той же оси, то, вероятно, лучше всего просто увеличить угол поворота вокруг этой оси и каждый раз пересчитывать новую матрицу с этого угла.Обратите внимание, что вы можете вычислить матрицу для вращения вокруг произвольной оси .Например, построение поворотов из углов Эйлера, как правило, не является ни необходимым, ни отличным решением (особенности, численно не идеальные, поведение не очень интуитивное).Существует перегрузка glm::rotate(), которая принимает ось и угол, который вы могли бы использовать для этого.

Если вам действительно нужно объединить много произвольных вращений вокруг произвольных осей, то использование Quaternions для представления ваших вращений потенциально можетбыть численно более устойчивым.Поскольку вы уже используете GLM, вы можете просто использовать кватернионов . этот урок может оказаться полезным .

...