Проблемы вращения объектов с помощью эйлеров — кватернионов

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

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

например:

Повернуть ось X на 50 градусов, Повернуть ось Y на 30 градусов -> Повернуть ось Y -50 градусов, Повернуть ось X -30 градусов. Работает.

Повернуть ось X на 50 градусов, Повернуть ось Y на 30 градусов -> Повернуть ось X на -50 градусов, Повернуть ось Y -30 градусов. Не имеет.

введите описание изображения здесь

Код:

void ComponentTransform::SetRotation(float3 euler_rotation)
{
float3 diff = euler_rotation - editor_rotation;
editor_rotation = euler_rotation;

math::Quat mod = math::Quat::FromEulerXYZ(diff.x * DEGTORAD, diff.y * DEGTORAD, diff.z * DEGTORAD);

quat_rotation = quat_rotation * mod;
UpdateMatrix();
}

Второй способ: Начинает хорошо вращаться вокруг оси, но после нескольких вращений он перестает правильно вращаться вокруг оси, но если я поверну его обратно, независимо от порядка вращения, он будет работать, а не как в первом случае.

введите описание изображения здесь

Код:

void ComponentTransform::SetRotation(float3 euler_rotation)
{
editor_rotation = euler_rotation;

quat_rotation = math::Quat::FromEulerXYZ(euler_rotation.x * DEGTORAD, euler_rotation.y * DEGTORAD, euler_rotation.z * DEGTORAD);

UpdateMatrix();
}

Остальной код:

#define DEGTORAD 0.0174532925199432957f

void ComponentTransform::UpdateMatrix()
{
if (!this->GetGameObject()->IsParent())
{
//Get parent transform component
ComponentTransform* parent_transform = (ComponentTransform*)this->GetGameObject()->GetParent()->GetComponent(Component::CompTransform);

//Create matrix from position, rotation(quaternion) and scale
transform_matrix = math::float4x4::FromTRS(position, quat_rotation, scale);

//Multiply the object transform by parent transform
transform_matrix = parent_transform->transform_matrix * transform_matrix;

//If object have childs, call this function in childs objects
for (std::list<GameObject*>::iterator it = this->GetGameObject()->childs.begin(); it != this->GetGameObject()->childs.end(); it++)
{
ComponentTransform* child_transform = (ComponentTransform*)(*it)->GetComponent(Component::CompTransform);
child_transform->UpdateMatrix();
}
}
else
{
//Create matrix from position, rotation(quaternion) and scale
transform_matrix = math::float4x4::FromTRS(position, quat_rotation, scale);

//If object have childs, call this function in childs objects
for (std::list<GameObject*>::iterator it = this->GetGameObject()->childs.begin(); it != this->GetGameObject()->childs.end(); it++)
{
ComponentTransform* child_transform = (ComponentTransform*)(*it)->GetComponent(Component::CompTransform);
child_transform->UpdateMatrix();
}
}
}

MathGeoLib:
Quat MUST_USE_RESULT Quat::FromEulerXYZ(float x, float y, float z) { return (Quat::RotateX(x) * Quat::RotateY(y) * Quat::RotateZ(z)).Normalized(); }

Quat MUST_USE_RESULT Quat::RotateX(float angle)
{
return Quat(float3(1,0,0), angle);
}

Quat MUST_USE_RESULT Quat::RotateY(float angle)
{
return Quat(float3(0,1,0), angle);
}

Quat MUST_USE_RESULT Quat::RotateZ(float angle)
{
return Quat(float3(0,0,1), angle);
}

Quat(const float3 &rotationAxis, float rotationAngleRadians) { SetFromAxisAngle(rotationAxis, rotationAngleRadians); }

void Quat::SetFromAxisAngle(const float3 &axis, float angle)
{
assume1(axis.IsNormalized(), axis);
assume1(MATH_NS::IsFinite(angle), angle);
float sinz, cosz;
SinCos(angle*0.5f, sinz, cosz);
x = axis.x * sinz;
y = axis.y * sinz;
z = axis.z * sinz;
w = cosz;
}

Любая помощь?

Благодарю.

0

Решение

Использование углов Эйлера и / или кватернионов добавляет некоторые ограничения, так как это создает особенности, которые, если не обработаны правильно, будут делать глупые вещи. К сожалению, почти все новые 3D-игры используют его неправильно. Вы можете обнаружить их по известным вещам, таким как:

  • иногда ваш взгляд попадает под совсем другой угол, которого там быть не должно
  • объект не может больше вращаться в каком-либо направлении
  • объект начинает вращаться вокруг разных осей, чем должен
  • вид прыгает вокруг полюса сингулярности
  • представление вращается или переворачивается, пока вы не переместитесь / не повернетесь снова (не тот, который вызван ошибкой оптической мыши)

Вместо этого я использую кумулятивные матрицы преобразования:

Прочитайте весь материал (особенно разницу между локальным и глобальным вращением), затем в последних 3 ссылках вы получили примеры того, как это сделать на C ++ (также прочитайте все 3, особенно о сохранении точности …).

Идея состоит в том, чтобы иметь матрицу, представляющую вашу систему координат объекта. И когда вы поворачиваете (мышью, клавиатурой, NAV, AI, …), вы поворачиваете матрицу (постепенно). То же самое касается движения. Таким образом, они не имеют ограничений или особенностей. Но и у этого подхода есть свои проблемы:

  • потеря точности со временем (см. пример сохранения точности, чтобы справиться с этим)
  • нет знания об углах Эйлера (однако углы могут быть вычислены из матрицы)

Оба разрешимы относительно легко.

Теперь, когда вы вращаетесь вокруг локальных осей, вы должны учитывать, что при каждом вращении вокруг некоторой оси вы меняете две другие. Поэтому, если вы хотите вернуться в исходное состояние, вам нужно изменить порядок вращения, потому что:

rotate around x by 30deg
rotate around y by 40deg

это не то же самое, что:

rotate around y by 40deg
rotate around x by 30deg

С накопительной матрицей, если вы хотите вернуться назад, вы можете итеративно вести свой корабль до тех пор, пока он не будет направлен в желаемые направления, или запомнить исходную матрицу и вычислить повороты, необходимые для выполнения по одной оси за раз. Или преобразовать разность матриц в кватернион и повторить этот единственный поворот …

0

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

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

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