Я вижу, что довольно часто возникают вопросы, связанные с этой основной проблемой, но все они связаны с особенностями данной функции или инструмента. Вот попытка создать канонический ответ, к которому мы можем отсылать пользователей, когда это произойдет, - с множеством анимированных примеров! :)
Допустим, мы делаем камеру от первого лица. Основная идея заключается в том, что он должен смотреть влево и вправо и наклоняться, чтобы смотреть вверх и вниз. Поэтому мы напишем немного кода, подобного этому (на примере Unity):
void Update() {
float speed = lookSpeed * Time.deltaTime;
// Yaw around the y axis using the player's horizontal input.
transform.Rotate(0f, Input.GetAxis("Horizontal") * speed, 0f);
// Pitch around the x axis using the player's vertical input.
transform.Rotate(-Input.GetAxis("Vertical") * speed, 0f, 0f);
}
или, может быть
// Construct a quaternion or a matrix representing incremental camera rotation.
Quaternion rotation = Quaternion.Euler(
-Input.GetAxis("Vertical") * speed,
Input.GetAxis("Horizontal") * speed,
0);
// Fold this change into the camera's current rotation.
transform.rotation *= rotation;
И это в основном работает, но со временем вид начинает изгибаться. Кажется, что камера вращается вокруг своей оси вращения (z), хотя мы только сказали ей вращаться по осям x и y!
Это также может произойти, если мы пытаемся управлять объектом перед камерой - скажем, это глобус, который мы хотим повернуть, чтобы осмотреться:
Та же проблема - через некоторое время Северный полюс начинает уходить влево или вправо. Мы даем вход по двум осям, но мы получаем этот запутанный поворот на третьей. И это происходит независимо от того, применяем ли мы все наши вращения вокруг локальных осей объекта или глобальных осей мира.
Во многих движках вы также увидите это в инспекторе - вращайте объект в мире, и внезапно числа меняются на оси, которую мы даже не трогали!
Итак, это ошибка двигателя? Как нам сказать программе, что мы не хотим, чтобы она добавляла дополнительное вращение?
Это как-то связано с углами Эйлера? Стоит ли вместо этого использовать кватернионы, матрицы вращения или базисные векторы?
источник
Ответы:
Нет, это не ошибка движка или артефакт определенного представления вращения (они тоже могут быть, но этот эффект применяется к любой системе, которая представляет вращения, включая кватернионы).
Вы обнаружили реальный факт о том, как вращение работает в трехмерном пространстве, и он отличается от нашей интуиции в отношении других преобразований, таких как перевод:
Когда мы составляем повороты по нескольким осям, мы получаем не просто общее / чистое значение, которое мы применили к каждой оси (как мы могли бы ожидать для перевода). Порядок, в котором мы применяем повороты, изменяет результат, так как каждое вращение перемещает оси, к которым применяются следующие повороты (если вращаются вокруг локальных осей объекта), или взаимосвязь между объектом и осью (если вращается вокруг мира оси).
Изменение отношения осей с течением времени может сбить с толку нашу интуицию о том, что каждая ось «должна» делать. В частности, определенные комбинации поворотов рыскания и тангажа дают тот же результат, что и вращение крена!
Вы можете убедиться, что каждый шаг вращается правильно относительно запрошенной оси - в наших обозначениях нет ни сбоев двигателя, ни артефактов, которые мешали бы или вводили в заблуждение наши данные - сферическая (или гиперсферическая / кватернионная) природа вращения просто означает, что наши преобразования "обертывают вокруг "друг на друга. Они могут быть ортогональны локально, для небольших вращений, но когда они накапливаются, мы обнаруживаем, что они не являются ортогональными в глобальном масштабе.
Это наиболее драматично и ясно для поворотов на 90 градусов, как те, что указаны выше, но блуждающие оси также проникают в течение многих малых поворотов, как показано в вопросе.
Итак, что нам с этим делать?
Если у вас уже есть система вращения по горизонтальной и вертикальной оси, один из самых быстрых способов устранения нежелательного крена - это изменить одно из вращений для работы на осях глобального или родительского преобразования вместо локальных осей объекта. Таким образом, вы не можете получить перекрестное загрязнение между двумя - одна ось остается абсолютно контролируемой.
Вот та же последовательность шага-рыскания-шага, которая стала креном в приведенном выше примере, но теперь мы применяем наш рыскание вокруг глобальной оси Y вместо объекта
Таким образом, мы можем исправить камеру от первого лица с помощью мантры «Pitch Localally, Yaw Globally»:
Если вы составляете свои вращения, используя умножение, вы бы перевернули левый / правый порядок одного из умножений, чтобы получить тот же эффект:
(Конкретный порядок будет зависеть от соглашений умножения в вашей среде, но левый = более глобальный / правый = более локальный - это обычный выбор)
Это равносильно сохранению требуемого общего рыскания и общего шага в качестве переменных с плавающей запятой, а затем всегда применяет суммарный результат сразу, создавая один новый кватернион ориентации или матрицу только из этих углов (при условии, что вы удерживаете
totalPitch
фиксированными):или эквивалентно ...
При использовании этого глобального / локального разделения у вращений нет шансов объединиться и повлиять друг на друга, потому что они применяются к независимым наборам осей.
Та же идея может помочь, если это объект в мире, который мы хотим вращать. Для примера, подобного глобусу, мы часто хотели бы инвертировать его и применить нашу рыскание локально (чтобы он всегда вращался вокруг своих полюсов) и наклонить его глобально (чтобы он наклонялся к нашему взгляду или от него, а не к Австралии или от нее) куда бы он ни указывал ...)
Ограничения
Эта глобальная / локальная гибридная стратегия не всегда является правильным решением. Например, в игре с трехмерным полетом / плаванием вы можете захотеть указывать прямо вверх / вниз и при этом иметь полный контроль. Но с этой настройкой вы нажмете замок карданного подвеса - ваша ось рыскания (глобальная вверх) станет параллельной вашей оси крена (локальный вперед), и вы не сможете смотреть влево или вправо, не поворачивая.
Вместо этого в подобных случаях вы можете использовать чистые локальные повороты, как мы начали в предыдущем вопросе (так что ваши элементы управления чувствуют то же самое, независимо от того, куда вы смотрите), что первоначально позволит прокрутиться некоторым броскам - но потом мы исправляем это.
Например, мы можем использовать локальные повороты для обновления нашего «прямого» вектора, а затем использовать этот прямой вектор вместе со ссылочным «восходящим» вектором для построения нашей окончательной ориентации. (Используя, например, метод Unity Quaternion.LookRotation или вручную создавая ортонормированную матрицу из этих векторов) Управляя вектором вверх, мы контролируем поворот или поворот.
Для примера полета / плавания вы захотите применять эти поправки постепенно с течением времени. Если оно слишком резкое, вид может отвлекать внимание. Вместо этого вы можете использовать текущий вектор игрока вверх и показывать его по вертикали, пока кадр не выровняется. Применение этого во время поворота иногда может быть менее тошнотворным, чем поворот камеры, когда элементы управления игрока находятся в режиме ожидания.
источник
TCVector::calcRotationMatrix(totalPitch, totalYaw, identity)
- это эквивалентно приведенному выше примеру «все сразу Эйлера».ПОСЛЕ 16 часов функций воспитания и ротации лол.
Я просто хотел, чтобы код был пустым, в основном, вращаясь по кругу, а также переворачивая фронт при повороте.
Или, другими словами, цель состоит в том, чтобы вращать рыскание и тангаж без какого-либо влияния на крен.
Вот те немногие, которые на самом деле работали в моем приложении
В единстве:
Теперь создайте сценарии в Visual Studio, выполните настройку и в конечном итоге получите следующее:
Обратите внимание, что все это сломалось для меня, когда я попытался изменить порядок воспитания. Или если я изменю Space.World для дочернего основного тона (родительский Yaw не возражает против поворота через Space.Self). Смешение. Я мог бы определенно использовать некоторые пояснения о том, почему мне пришлось взять локальную ось, но применить ее через мировое пространство - почему Space.Self все разрушает?
источник
me.Rotate(me.right, angle, Space.World)
это то же самоеme.Rotate(Vector3.right, angle, Space.Self
, что и ваши вложенные преобразования эквивалентны примерам "Pitch Localally, Yaw Globally" в принятом ответе.