вопрос, сочетающий 2 угла Эйлера с GLM

У меня есть следующий код:

#define GLM_ENABLE_EXPERIMENTAL

#include <iostream>
#include <glm/glm.hpp>
#include <glm/ext.hpp>

// combines 2 XYZ euler angles given in degrees
glm::vec3   EulerCombine(const glm::vec3& first, const glm::vec3& second)
{
glm::vec3 output;
glm::mat4 m1 = glm::eulerAngleXYZ(glm::radians(first.x), glm::radians(first.y), glm::radians(first.z));
glm::mat4 m2 = glm::eulerAngleXYZ(glm::radians(second.x), glm::radians(second.y), glm::radians(second.z));
glm::extractEulerAngleXYZ(m2 * m1, output.x, output.y,  output.z);
return glm::degrees(output);
}

// applies the XYZ euler rotation on p
glm::vec3   EulerRotate(const glm::vec3& p, const glm::vec3& euler)
{
glm::vec3 output;
output = glm::rotateX(p, glm::radians(euler.x));
output = glm::rotateY(output, glm::radians(euler.y));
output = glm::rotateZ(output, glm::radians(euler.z));
return output;
}

int     main(void)
{
glm::vec3 euler1(30, 20, 90); // euler angles in degrees
glm::vec3 euler2(20, 30, 10);
glm::vec3 euler3 = EulerCombine(euler1, euler2);
glm::vec3 p(-10, 7, 23);

glm::vec3 result1 = EulerRotate(EulerRotate(p, euler1), euler2);
glm::vec3 result2 = EulerRotate(p, euler3);

std::cout << result1.x << " " << result1.y << " " << result1.z << std::endl;
std::cout << result2.x << " " << result2.y << " " << result2.z << std::endl;
}

но это печатает:

17.9056 -6.99702 17.5622
17.2369 4.15094 19.0699

что является проблемой, потому что оба result1 а также result2 (печатные векторы) должны быть одинаковыми.

Я чувствую себя как EulerCombine функция должна быть правильной, потому что это почти исключительно glm, но тогда это будет означать EulerRotate было неверно — но я думал, что угол Эйлера XYZ означал, что вы поворачиваете его по оси X, а затем по оси Y, а затем по оси Z.

В чем дело?

1

Решение

Как ОП уже подозревал: реализация EulerRotate() неправильно. Это должно быть вместо:

glm::vec3 EulerRotate(const glm::vec3& p, const glm::vec3& euler)
{
return glm::rotateX(glm::rotateY(glm::rotateZ(p, glm::radians(euler.z)),
glm::radians(euler.y)), glm::radians(euler.x));
}

который также может быть записан (более читабельно) как:

glm::vec3 EulerRotate(const glm::vec3& p, const glm::vec3& euler)
{
const glm::vec3 p1 = glm::rotateZ(p, glm::radians(euler.z));
const glm::vec3 p2 = glm::rotateY(p1, glm::radians(euler.y));
const glm::vec3 p3 = glm::rotateX(p2, glm::radians(euler.x));
return p3;
}

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

MRxyz знак равно MгХ · MRy · MRZ

и поэтому:

P ‘= MRxyz · P = MгХ · MRy · MRZ · П

Это

P ‘= P вращается вокруг Z, вращается вокруг Y, вращается вокруг X.

Как бы просто это ни звучало — делать это правильно в практической работе время от времени сводит меня с ума. Вращения в трехмерном пространстве не коммутативны → порядок очень важен.

Я подготовил образец, чтобы продемонстрировать это. Как у меня не было glm под рукой (и не хотел устанавливать его на моей стороне), я использовал собственный код (однажды я подготовился к другому примеру) и попытался максимально приблизиться к коду OP:

#include <iostream>
#include "linMath.h"
double radians(double);
Vec3 degrees(Vec3 angles);
Mat4x4 eulerAngleXYZ(double rX, double rY, double rZ);
void extractEulerAngles(const Mat4x4 &mat, double &rX, double &rY, double &rZ);
Vec3 rotateX(const Vec3 &p, double angle);
Vec3 rotateY(const Vec3 &p, double angle);
Vec3 rotateZ(const Vec3 &p, double angle);

// combines 2 XYZ euler angles given in degrees
Vec3 eulerCombine(const Vec3 &first, const Vec3 &second)
{
const Mat4x4 mat1 = eulerAngleXYZ(radians(first.x), radians(first.y), radians(first.z));
const Mat4x4 mat2 = eulerAngleXYZ(radians(second.x), radians(second.y), radians(second.z));
Vec3 output;
extractEulerAngles(mat2 * mat1, output.x, output.y, output.z);
return degrees(output);
}

// applies the XYZ euler rotation on p
Vec3 eulerRotate(const Vec3 &p, const Vec3 &euler)
{
#ifndef FIX // Theo:
Vec3 output;
output = rotateX(p, radians(euler.x));
output = rotateY(output, radians(euler.y));
output = rotateZ(output, radians(euler.z));
return output;
#else // Dirk:
return rotateX(rotateY(rotateZ(p, radians(euler.z)), radians(euler.y)), radians(euler.x));
#endif // FIX
}

int main()
{
Vec3 euler1(30, 20, 90); // euler angles in degrees
Vec3 euler2(20, 30, 10);
Vec3 euler3 = eulerCombine(euler1, euler2);
Vec3 p(-10, 7, 23);

Vec3 result1 = eulerRotate(eulerRotate(p, euler1), euler2);
Vec3 result2 = eulerRotate(p, euler3);

std::cout << result1.x << " " << result1.y << " " << result1.z << std::endl;
std::cout << result2.x << " " << result2.y << " " << result2.z << std::endl;
// done
return 0;
}

double radians(double angle) { return degToRad(angle); }

Vec3 degrees(Vec3 angles)
{
return Vec3(radToDeg(angles.x), radToDeg(angles.y), radToDeg(angles.z));
}

Mat4x4 eulerAngleXYZ(double rX, double rY, double rZ)
{
return Mat4x4(InitRotX, rX) * Mat4x4(InitRotY, rY) * Mat4x4(InitRotZ, rZ);
}

void extractEulerAngles(const Mat4x4 &mat, double &rX, double &rY, double &rZ)
{
decompose(mat, RotX, RotY, RotZ, rX, rY, rZ);
}

Vec3 rotateX(const Vec3 &p, double angle)
{
const Vec4 p_ = Mat4x4(InitRotX, angle) * Vec4(p, 1.0);
return Vec3(p_.x, p_.y, p_.z);
}

Vec3 rotateY(const Vec3 &p, double angle)
{
const Vec4 p_ = Mat4x4(InitRotY, angle) * Vec4(p, 1.0);
return Vec3(p_.x, p_.y, p_.z);
}

Vec3 rotateZ(const Vec3 &p, double angle)
{
const Vec4 p_ = Mat4x4(InitRotZ, angle) * Vec4(p, 1.0);
return Vec3(p_.x, p_.y, p_.z);
}

Сначала я попробовал код с eulerRotate() функция, которая похожа на порядок преобразования OP и получила следующий вывод:

17.9056 -6.99702 17.5622
17.2369 4.15094 19.0698

Кажется, я правильно воспроизвел проблему ОП. Теперь я определил FIX использовать правильный порядок преобразования и получить следующий вывод:

12.1019 -22.7589 3.68476
12.1019 -22.7589 3.68476

Живая Демо на Wandbox

Теперь оба вычисления дают тот же результат, что и ожидалось.

Я думал, что угол Эйлера XYZ означает, что вы поворачиваете его по оси X, а затем по оси Y, а затем по оси Z.

Не совсем. Как показано выше, это должно интерпретироваться справа налево.

3

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector