умножение
По крайней мере, с точки зрения реализации кватернионов в Unity, порядок умножения, описанный в этом вопросе, неверен. Это важно, потому что трехмерное вращение не является коммутативным .
Итак, если я хочу повернуть объект, rotationChange
начиная с его, currentOrientation
я напишу это так:
Quaternion newOrientation = rotationChange * currentOrientation;
(т.е. преобразования преобразуются влево - то же самое, что и матричное соглашение Unity. Крайнее правое вращение применяется первым / на «самом локальном» конце)
И если бы я хотел преобразовать направление или вектор смещения с помощью вращения, я бы написал это так:
Vector3 rotatedOffsetVector = rotationChange * currentOffsetVector;
(Unity выдаст ошибку компиляции, если вы сделаете обратное)
смешивание
В большинстве случаев вы можете сойти с поворотов Лерпинга. Это потому, что угол, используемый «под капотом» в кватернионе, составляет половину угла поворота, что делает его существенно ближе к линейному приближению Lerp, чем что-то вроде Matrix (что в общем случае не будет Lerp хорошо!). Проверьте около 40 минут этого видео для более подробного объяснения .
Единственный случай, когда вам действительно нужен Slerp, это когда вам нужна постоянная скорость во времени, например, интерполяция между ключевыми кадрами на временной шкале анимации. В тех случаях, когда вы просто заботитесь о том, чтобы вывод был промежуточным между двумя входами (например, смешивание слоев анимации), тогда обычно Lerp работает достаточно хорошо.
Что-то еще?
Скалярное произведение двух единичных кватернионов дает косинус угла между ними, так что вы можете использовать продукт многоточия в качестве меры сходства , если вам нужно сравнить ротацию. Это немного неясно, поэтому для более разборчивого кода я бы часто использовал вместо этого Quaternion.Angle (a, b) , который более четко выражает то, что мы сравниваем углы в знакомых единицах (градусах).
Эти типы удобных методов, которые Unity предоставляет для кватернионов, очень полезны. Почти в каждом проекте я использую этот по крайней мере несколько раз :
Quaternion.LookRotation(Vector3 forward, Vector3 up)
Это строит кватернион, который:
- вращает локальную ось z +, чтобы указывать точно вдоль
forward
аргумента вектора
- вращает локальную ось y +, чтобы указать как можно ближе к
up
аргументу вектора, если он задан, или к, (0, 1, 0)
если пропущен
Причина, по которой «вверх» становится «как можно ближе», заключается в том, что система переопределена. Перед лицом z + forward
используется две степени свободы (т. Е. Рыскание и тангаж), поэтому у нас остается только одна степень свободы (крен).
Я нахожу довольно часто, что я хочу что-то с противоположными свойствами точности: я хочу, чтобы локальная y + указывала точно вдоль up
, а локальная z + была как можно ближе к forward
оставшейся свободе.
Это происходит, например, при попытке сформировать относительную камеру для системы координат для ввода движения: я хочу, чтобы мое локальное направление вверх оставалось перпендикулярным полу или наклонной поверхности в нормальном состоянии, поэтому мой ввод не пытается туннелировать персонажа в местность или левитировать их от этого.
Вы также можете получить это, если хотите, чтобы корпус башни танка был направлен на цель, не отрываясь от корпуса танка при наведении вверх / вниз.
Для этого мы можем создать собственную удобную функцию, используя LookRotation
для тяжелой работы:
Quaternion TurretLookRotation(Vector3 approximateForward, Vector3 exactUp)
{
Quaternion rotateZToUp = Quaternion.LookRotation(exactUp, -approximateForward);
Quaternion rotateYToZ = Quaternion.Euler(90f, 0f, 0f);
return rotateZToUp * rotateYToZ;
}
Здесь мы сначала поворачиваем локальный y + на z +, а локальный z + на y-.
Затем мы поворачиваем новый z + в нашем направлении вверх (таким образом, чистый результат - это локальные точки y + непосредственно вдоль exactUp
), а новый y + как можно ближе к отрицательному направлению вперед (таким образом, чистый результат - локальные точки z + как можно ближе вдоль approximateForward
)
Другой удобный удобный метод Quaternion.RotateTowards
, который я часто использую так:
Quaternion newRotation = Quaternion.RotateTowards(
oldRotation,
targetRotation,
maxDegreesPerSecond * Time.deltaTime
);
Это позволяет нам приближаться targetRotation
с постоянной, управляемой скоростью независимо от частоты кадров - это важно для поворотов, которые влияют на результат / справедливость игровой механики (например, поворот персонажа или наличие турели на игроке). Наивно Lerping / Slerping в этой ситуации может легко привести к случаям, когда движение становится более быстрым при высоких частотах кадров, влияя на игровой баланс. (Это не значит, что эти методы неправильны - есть способы использовать их правильно без изменения справедливости, это просто требует осторожности. RotateTowards
Предоставляет удобный ярлык, который позаботится об этом для нас)
n
разные ориентации (отношения, позы и т. Д.). Затем вы можете усреднить их, используя веса, эффективно обобщая slerp / lerp. Вы также можете преобразовать кватернион в ротор, что эквивалентно применению угловой скорости в течение определенного времени для твердого тела. Следовательно, вы можете также описать интеграцию угловой скорости с кватернионами. Вы также можете оценить, насколько различны две ориентации (вычислить длину дуги, натянутой двумя кватернионами на гиперсфере).