Сценарий
Учитывая нормализованный кватернион A_q
и нормализованный вектор направления B_v
, мы рассчитываем новый поворот C_q
, который будет вращать A_q
в «грань» внаправление B_v
.Для пояснения, пусть D_q = C_q * A_q
так, что (1,0,0)
с поворотом на D_q
будет равно B_v
.
NB. Для удобства обозначения отныне мы будем ссылаться на (1,0,0)
, повернутый любым кватернионом, как символ этого кватерниона с суффиксом _v
, то есть (1,0,0)
, повернутый на A_q
, становится A_v
.
Демонстрация
Я реализовал такой сценарий здесь (интерактивный), с источником здесь и также прикрепил gif ниже.
Моя проблема
Моя проблема в том, что когда A_v
и B_v
близки к противоположным (или кажутся приближающимися к антипараллельным), D_q
демонстрирует внезапный поворот / крен, что нежелательно.Изображение выше демонстрирует это.
Код, о котором идет речь
Он находится в C ++ API UE4, но он должен быть достаточно независимым от языка / фреймворка для анализа:
FTransform UMWEUtil::FindNewRotation(FTransform A_t, FVector B_v)
{
FQuat A_q = A_t.GetRotation();
FVector A_v = A_q.GetForwardVector();
A_v.Normalize();
B_v.Normalize();
FVector AxisOfRotation = FVector::CrossProduct(A_v, B_v).GetSafeNormal();
float Rads = FMath::Acos(FVector::DotProduct(A_v, B_v));
FQuat C_q(AxisOfRotation, Rads);
FQuat D_q = C_q * A_q;
return FTransform(D_q);
}
Мои вопросы
- Почему возникает этот внезапный артефакт крена / закручивания?
- Я предполагаю, что это из-за перекрестного произведения
A_v X B_v
изменения полушарий.Но тогда, почему это происходит только один раз, а не дважды (видно, что перекрестное произведение, A_v X B_v
перемещает полусферы дважды за полный оборот)? - Наконец, решение, которое я нашел, состоит в том, чтобы разложить
D_q
в кватернионы качания / кручения и используйте только кватернион качания (таким образом, удаляя внезапный поворот).Тем не менее, это похоже на пластырь из-за моего непонимания проблемы, есть ли другое решение, которое приведет к плавному переходу?
Мое решение (может быть, ненужное?)
FTransform UMWEUtil::FindNewRotation2(FTransform A_t, FVector B_v)
{
FQuat A_q = A_t.GetRotation();
FVector A_v = A_q.GetForwardVector();
A_v.Normalize();
B_v.Normalize();
FVector AxisOfRotation = FVector::CrossProduct(A_v, B_v).GetSafeNormal();
float Rads = FMath::Acos(FVector::DotProduct(A_v, B_v));
FQuat C_q(AxisOfRotation, Rads);
FQuat D_q = C_q * A_q;
FQuat OutSwing;
FQuat OutTwist;
D_q.ToSwingTwist(FVector::ForwardVector, OutSwing, OutTwist);
return FTransform(OutSwing);
}