Геймдизайнер-телепат
0,0
рейтинг
26 июня 2013 в 13:24

Разработка → Каверзные кватернионы



Отгадайте загадку: в четырёх измерениях сидит и комплексными числами воротит?

Подсказка: это вектор со скаляром. И вещественная матрица. И придумал его Гамильтон.

Не помогло? Ну что вы, это же элементарно! Это кватернион! Кватернионы используют для записи вращений в робототехнике, игровых движках, ПО для моделирования и вообще везде, где не нужны проблемы с углами Эйлера или матрицами. Если вас испугала путаница выше с разными представлениями кватерниона, то можете быть спокойны. Кватернионы очень просты в использовании и их внутреннее строение может понадобиться только в очень редких случаях, где нужна тонкая оптимизация. В остальное время с помощью кватернионов можно крутить всё что угодно и как угодно, и оно будет плавно и красиво интерполироваться без шарнирных замков.

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

Кватернион имеет хитрое внутреннее строение. Его можно записать с помощью четырёх чисел: x, y, z для вектора и w для поворота. Сложность заключается в том, что в дополнение к ним ещё есть три мнимые единицы, такие, что: i2 = j2 = k2 = ijk = −1. Целиком запись выглядит следующим образом: q = w + x*i + y*j + z*k

Из-за единиц сложение-умножение кватернионов вручную достаточно муторно, но, к счастью, в программировании они не особо важны. Все операции обычно производятся над кватернионами в целом, и лишь изредка используются те четыре числа. Кроме того, во многих библиотеках имеются специальные конструкторы, которые позволяют получить кватернион из более понятных структур, например Quaternion.Euler в Unity3d или Quaternion.CreateFromYawPitchRoll в XNA.

В работе с кватернионами чаще всего приходится их умножать друг на друга. Это очень полезная операция, ведь при умножении одного кватерниона на другой получается первое вращение, повёрнутое на второе. Важно помнить, что умножение кватернионов некоммутативно, а значит важен порядок операндов. То есть q1*q2 это не то же самое, что и q2*q1. На картинке ниже можете посмотреть на разницу. Слева: Quaternion.Euler(60, 0, 60) * Quaternion.Euler(0, 60, 0), справа: Quaternion.Euler(0, 60, 0) * Quaternion.Euler(60, 0, 60). Цветными линиями показаны пути локальных осей каждого самолётика.



На практике работа с кватернионами выглядит так:

var rightTurn = Quaternion.Euler(0, 90, 0); // Создаём новый поворот направо
car.rotation = car.rotation*rightTurn; // Крутим

В примере выше машинка car поворачивается направо в локальных координатах. Если машина едет по наклонной плоскости, или даже вверх ногами, то она всё равно корректно повернётся куда нужно. Переменную rightTurn можно использовать любое количество раз, вот так, например, выглядит поворот на 360 градусов:

car.rotation = car.rotation*rightTurn*rightTurn*rightTurn*rightTurn;

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

Если бы вы вдруг захотели смоделировать перемещение по разнообразным выпуклым поверхностям как в Super Mario Galaxy с помощью углов Эйлера, то вам пришлось бы долго ломать голову. Всё потому, что они были придуманы для указания конкретной ориентации объекта в пространстве, а не для анимации. С кватернионами всё гораздо проще, и они замечательно интерполируются по кратчайшим путям, тогда как углы Эйлера начинают юлить из-за иерархии поворотов и частенько застревают в шарнирном замке, про который я писал в этой статье. На анимации ниже можете посмотреть как сильно отличается их сферическая интерполяция. Слева кватернионы, справа углы Эйлера.



Если вам всё-таки интересно как кватернионы выглядят внутри, то можете посмотреть на таблицу ниже.
w x y z Вращение
1 0 0 0 нет вращения
0 1 0 0 180° вокруг оси X
sqrt(0.5) sqrt(0.5) 0 0 90° вокруг оси X
sqrt(0.5) -sqrt(0.5) 0 0 -90° вокруг оси X

Значения в таблице получаются из следующей формулы:

[w, x, y, z] = [cos(alpha/2), sin(alpha/2)*vx, sin(alpha/2)*vy, sin(alpha/2)*vz]

Где alpha — это угол вращения, а vx, vy, vz — вектор оси вращения.

Результатом сложения кватернионов является промежуточное вращение. Сложение кватернионов элементарно, достаточно просто сложить их компоненты. Возьмём, например, q1 = 1 + 0i + 0j + 0k (нулевое вращение из таблицы выше) и q2 = 0 + 1i + 0j + 0k (180° вокруг оси X). Их суммой будет q3 = 1 + 1i + 0j + 0k, т. е. 90° вокруг оси X. К сожалению, в Unity3d оператор сложения не реализован для кватернионов.

Если вы хотите узнать побольше о кватернионах, то советую начать с английской Википедии. Также можете покопаться в Wolfram|Alpha, он выдаёт тонны попутной информации и вообще много умеет. Также можете посмотреть на разницу между углами Эйлера и кватернионами в интерактивной демонстрации по ссылкам ниже.

Видео

Unity Web Player | Windows | Linux | Mac | Исходники на GitHub
Мышкой вращать сцену, AD, WS и QE — вращение вокруг осей, Esc — выход, остальные кнопки указаны на экране.
Для пользователей Linux: Сделайте файл Quaternions исполняемым с помощью «chmod +x Quaternions» и запускайте.
Даниил Басманов @BasmanovDaniil
карма
190,2
рейтинг 0,0
Геймдизайнер-телепат

Похожие публикации

Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (47)

  • +1
    Огромное спасибо за статью! Очень интересно :)
  • 0
    Спасибо! Теперь чуть более понятно)
  • +1
    Если кому интересно, то вот тут есть небольшой FAQ по кватернионам и матрицам — умножение, конвертация в/из угла вращения и оси, Эйлеровы углы вращения.
  • 0
    Почему вы так настаиваете на том, что я пользуюсь углами Эйлера?
    Мне кажется что квантерион это матрица 4х4! И с ними можно разобраться (узнать что у них внутри и как действует).
    • +1
      Не совсем так. Сам по себе кватернион — это четыре числа. А вот эти четыре числа уже можно легко конвертировать в матрицу 3х3 или 4х4.
      Если говорить простым языком: кватернион — это положение объекта в пространстве относительно некой системы координат; а углы Эйлера — это на какие углы вокруг каких осей надо повернуть объект, чтобы он принял желаемое положение.
    • –2
      Не видел ни одного тридэшного приложения, где можно было бы вводить вращения кватернионами или даже матрицами. Везде конечное представление выводится с помощью эйлеров. Так что в некотором роде все пользуются именно ими.
      Да, начать разбираться можно, но сложно будет остановиться. Сначала сложение и умножение, потом логарифмы, потом кватернионные группы… Лучше просто пользоваться как есть :)
      • –1
        Урок 5. «Матрицы». — 77 страница (по 86-ую, немного).

        Станислав Горнаков. «DirectX 9. Уроки программирования на C++». СПб.: БХВ 2005. — 400;
        Не самый авторитетный для меня источник. Но моя первая книга о 3D программировании.

        Книга о DirectX и как им пользоваться. Сразу в матрицы, о квантерионах и речи нет. Углы Эйлера не упомянуты.
        Их (углы Эйлера) может быть интуитивно хочется использовать после школьной геометрии, но их возможности так же «школьны».
        Матрицы — одно и тоже что квантерион, если не более гибкий инструмент. Ими пользуются для анимирования объектов в 3D пространстве.
        Квантерион — это более научный, из мира физики термин.
        • –1
          Матрицы — одно и тоже что квантерион

          Бред. Матрицы — вращение представленное углами эйлера. Ничего общего с кватернионами не имеет, разве что кватернион можно представить в виде матрицы вращения, которое по сути будет той же матрицей вращения со всеми вытекающими недостатками.

          • 0
            А какая связь между матрицами и углами Эйлера? Матрица — координаты векторов, в которые переходят базисные векторы при данном вращении. Да, её можно разложить в произведение трёх матриц поворотов вокруг некоторых координатных осей (и тогда получатся углы Эйлера), но ведь можно этого и не делать — оставить матрицу девятью числами.
            • 0
              Да, вы правы, точнее сказать: матрица поворота описывает вращение вокруг произвольного вектора.
              • 0
                Не совсем так. Матрица поворота описывает вообще произвольное вращение, которое, в случае трёхмерного пространства случайно оказывается вращением вокруг какого-то вектора (а в случае четырёхмерного, например, уже не обязательно). И извлечь этот вектор из матрицы не очень просто. Вот кватернион — гораздо лучше: в нём и вектор, и угол записаны почти в явном виде.
      • 0
        Не видел ни одного тридэшного приложения, где можно было бы вводить вращения кватернионами или даже матрицами. Везде конечное представление выводится с помощью эйлеров. Так что в некотором роде все пользуются именно ими.


        Отчасти. В 3ds max и все внутренние представление вращения — только через кватернионы. В блендере и Maya думаю то же. Ибо без этого никуда, Гимбал лок — это большая проблема и игнорить её нельзя. В частности для анимации все используют кватернионы сейчас.

        А для юзера обычно дается упрощение в виде углов Эйлера, которое на самом деле добавляет вращение кватерниону внутри. Или делают отдельно два способа задания вращения. В Майе вроде так.
  • 0
    Все-таки, несовсем понятно, почему кватернионы лучше, чем углы Эйлера или матрицы поворотов. И всегда ли они лучше, кстати?
    • +4
      Кватернионы лучше, потому что их умножение непрерывно: если мы повернём кватернион на малый угол, то его коэффициенты изменятся незначительно. Далее, их применение, в отличие от углов Эйлера, не требует тригонометрии — достаточно умножения. Можно легко найти кватернион перехода из одного положения в другое (поделив кватернионы друг на друга). И рассчитать наиболее плавную траекторию поворота. С углами Эйлера всё это проделать заметно сложнее.
      А углы Эйлера лучше тем, что они нагляднее. Проще задать координаты «из головы». И проще понять смысл преобразования, если оно задано углами Эйлера. Компьютеру же кватернионы удобнее. Хотя от матриц он тоже не откажется :)
      • 0
        Спасибо, это уже понятнее! Но разве чтобы найти коэффициенты кватерниона не потребуется брать косинусы и синусы от угла поворота?
        • +1
          Это придётся сделать один раз. И только для одного угла (точнее, даже, от его половинки :D ) — при генерации этого кватерниона. Для поворотов на малый угол тригонометрия уже не нужна, там работают приближения sin(x)=x, cos(x)=1. Потребуется только один квадратный корень.
          • 0
            Ага, понял.
            А как кватернионы спасают от gimbal lock?
            • 0
              Тем что вектор умножается на не на матрицу поворота, а на кватернион. И тем что каждая такая трансформация не зависит от предыдущей.
    • 0
      Углы Эйлера для анимации можно вообще не рассматривать, у них есть шарнирный замок, который отсутствует у матриц и кватернионов. Запись кватерниона немного компактнее матрицы, четыре числа против девяти, хотя на нынешнем железе память давно не проблема. Интерполяция кватернионов проще. Рано или поздно накапливаются ошибки вычислений над дробями и нужно от них избавляться, нормализовать кватернион тоже проще, чем ортогонализировать матрицу. Внешний вид кватерниона менее презентабелен чем у углов Эйлера, но это же касается и матриц. Умножение кватернионов быстрее. Матрицы и кватернионы имеют разное строение и разные возможности по оптимизации, иногда матрицы не требуют операций там, где они нужны у кватернионов. Если мне не изменяет память, OpenGL не умеет работать с кватернионами и их нужно конвертировать в матрицу. Короче говоря, если если нужно много-много вращений, то с кватернионами будет меньше проблем, если нужно просто указать ориентацию объекта, то матрицы могут быть быстрее.
      • 0
        Насчёт того, что умножение кватернионов быстрее — не очевидно. Конечно, если брать только повороты, то два кватерниона перемножаются за 16 умножений, а две матрицы за 27. Но если добавить сдвиги? Для матриц потребуется всего 36 умножений. А сколько для пары (кватернион, сдвиг)? На первый взгляд, получается 44. Если быстренько перевести один из кватернионов в матрицу, получится 35, но это не считая умножений на двойки. Или есть способ сделать это быстрее?
      • 0
        Согласен с Mrrl.
        Еще, есть такое понятие в анимировании, как «Матрица гомографии». Это неравномерное искажение объекта.
        Можно ли такое сделать квантерионами? А перспективное преобразование. Повороты, сдвиги, масштабирование — это только основные возможности матриц.

        Так же из собственного опыта могу сказать(если кому интересно), что коэффициенты матрицы 3x3( входящей в состав 4x4 используемой в 3D) — это вектора ортов оX, oY, oZ пространства производимого данной матрицей. И так далее. Зная такие вещи, программирование матрицами не страшно.
        • 0
          С другой стороны, то, что пространство коэффициентов кватернионов всего лишь четырёхмерно, помогает при решении, например, таких задач, как поиск наилучшего совмещения двух и более сцен или поиск перемещения с данными свойствами. Недавно мне понадобилось перебрать все возможные повороты сферы с шагом в несколько градусов. Построить достаточно регулярную решетку на гиперсфере было несложно. Как бы я перебирал ортогональные матрицы — даже боюсь представить :) Наверное, через углы Эйлера…
          • 0
            Интересная тема. Нужно изучить более подробно. Почитаю про квантерионы еще. Уверен в них тоже есть свой наглядный математико-геометрический смысл.
            • 0
              Прежде всего — с их помощью удобно представить себе множество всех поворотов пространства в виде обычного трёхмерного шара. Центр шара — тождественное преобразование, точки границы — повороты на 180 градусов (симметричные точки отождествляются), остальные точки внутри — повороты на разные углы вокруг своих радиус-векторов. Например, множество поворотов, переводящих один вектор в другой, образует кривую, похожую на половинку эллипса, соединяющую две противоположные точки сферы.
              • 0
                Спасибо.
                Надо написать конвертор (прямой и обратный если существуют) и тогда думаю все встанет на свои места.
                • 0
                  Между кватернионами и матрицами?
                  Из кватерниона в матрицу сравнительно легко (не так давно где-то тут даже приводился его код). А вот обратно — сложнее, придётся искать собственный вектор матрицы.
                • 0
                  Чуть выше приводил ссылку. Вот тут подробно расписана конвертация в обе стороны.
      • +1
        Спасибо за подробный ответ.

        Признаюсь, меня больше интересует приложение кватернионов для робототехники, а не для анимации. Я как-то краем уха слышал, что на кватернионах проще считать кинематику, но очень сложно считать динамику, например.
  • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      ijk = ij*k=k*k = -1
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          i — это симметрия относительно оси Ox, а j — симметрия относительно Oy. Их суперпозиция будет как раз симметрией относительно Oz, т.е. k:
          (x,y,z) -> (x,-y,-z) -> (-x,-y,z).
          А вообще, так эти кватернионы определили, иначе у них никак не получалось существования деления.
          • 0
            Любопытно, что для кватернионов существует два деления (a на b): деление левое (b^(-1)*a) и правое (a*b^(-1))
          • НЛО прилетело и опубликовало эту надпись здесь
            • +1
              Это и есть мнимые единицы. Любое из множеств кватернионов a+b*i, a+b*j, a+b*k и даже a+b*(i+2*j+2*k)/3 ведёт себя как множество комплексных чисел с вещественной частью a и мнимой частью b. В кватернионах уравнение q^2=-1 имеет в качестве решений всю сферу {i*x+j*y+k*z: x^2+y^2+z^2=1}.
        • 0
          Это либо определение, либо следствие из определения. Что такое кватернион? Это четверка вещественных чисел и правилами умножения и сложения (кольцо). Немного методологии. Для того, чтобы кольцо было полем (расширением поля комплексных чисел, с реализацией операции «взять обратный элемент» и без «делителей нуля») «умножение» определяется немного не так и отсюда вытекает, в том числе, что i*j должно равняться k.
          • 0
            Кватернионы образуют тело в силу некоммутативности умножения.
  • 0
    Странно, что в статье нет самой главной формулы — как кватернион применить к вектору (или я её не увидел). Если у нас есть кватернион q, то его действие на вектор v равно q*v*q^(-1), где v — кватернион с нулевой действительной частью. Или q^(-1)*v*q, в зависимости от нашей интерпретации (что сейчас выбрано в компьютерной графике, к сожалению, не знаю). q^(-1) — кватернион обратный данному, его можно найти по формуле
    [w,x,y,z]^(-1)=[w,-x,-y,-z]/(w^2+x^2+y^2+z^2).
  • +1
    А чего про интерполяцию кватернионов не слова, ведь главное для анимации не умножение — сложение а старый добрый slerp.
  • –2
    Вы бы лучше исходники выложили: вот у меня, например, линукс. И я непонятный бинарник запускать не собираюсь. Да и толку от бинарника — 0!
    И еще: не факт, что он вообще запустится у меня (если только вы его полностью статическим не сделали).

    По поводу кватернионов у меня такой вопрос: натыкались ли вы на приличные свободные библиотеки (GPL3 или хотя бы GPL2), реализующие операции с кватернионами?
    • 0
      Последний абзац: Исходники на GitHub.

      Вот например библиотека Boost.Quaternions. Сам не пользовался, но в остальном буст хороший, наверное и кватернионы не плохи.
      • 0
        А можно пояснить: что это? Скачал исходники и без понятия, что с ними делать: ни Makefile'а, ни autotools, ни cmake…
        Что это?

        Насчет буста: я только чистыми сями пользуюсь, мне буст не нужен.
        • 0
          Это проект Unity3d, сейчас исправлю описание. Боюсь, что у вас не получится собрать его под линуксом, я записал для вас видео

          • 0
            Добрый день, спасибо за статью.
            Я пытаюсь посчитать (кинетическую) энергию вращения rigidbody в Unity3d, нигде прямых указаний на формулы не нашел.
            А в формуле с необходимостью употребляется кватернион вращения главных моментов инерции, так что…
            Не подскажите ли, верна ли формула, которую я попытался «воссоздать» сам:
            Vector3 av = rigidbody.angularVelocity;
            Vector3 itr = rigidbody.inertiaTensorRotation;
            Vector3 it = rigidbody.inertiaTensor;
            
            float E = Vector3.Dot(av, itr * Vector3.Scale(it, Quaternion.Inverse(itr) * av)) / 2f;
            


            • 0
              Вы про эту формулу из википедии?
              image
              Угловая скорость есть, тензор инерции тоже есть. Что такое тензор инерции и зачем нужно его вращение я не знаю, никогда не сталкивался, так что тут я вам не помощник. А зачем вам понадобилась энергия вращения?
              • 0
                Примерно про эту. Если считать, что тензор инерции считается применением к вектору главных моментов инерции кватерниона поворота этого самого вектора, то получается примерно та формула, что я написал.
                Вот только нигде в документации в явном виде не написано, как правильно «комбинировать» кватернион поворота и вектор главных моментов. Умножая всё это дважды на вектор скорости. Затруднение.

                Что касается энергии вращения, то я с удивлением обнаружил, что в физике Unity энергию столкновения надо считать самому.
                Мой способ расчета потери энергии при соударении:
                Для этого перед соударением (на последнем физическом апдейте) запомнить угловую и линейную скорость обоих тел, после выхода из рассчитать потерю энергии: кинетическую (линейную + вращения) энергию обоих тел после столкновения вычесть из той же энергии до столкновения. Таким образом можно определить, сколько энергии в системе сталкивающихся тел «потерялось», то есть перешло в деформации, излучение, тепло.
                Думаю, вполне очевидно, почему считать изменение энергии только для одного из сталкивающихся тел недостаточно.

                После этого можно получить количество энергии в единицу времени для каждого тела (впрочем, вопрос распределения энергии между телами тоже нетривиален). Расчет полной кинетической энергии невозможен без учета энергии вращения, отсюда и задача.
                Ну а энергия столкновения нужна, например, для «приближенного к реальности» расчета повреждений.

                Кстати, вопрос вдогонку: При событии OnCollisionEnter тела уже столкнулись, их скорости изменены по сравнению с прошлым кадром. Нет ли у Вас соображений по поводу того, как получить скорости rigidbody любого «физического» тела без прикрепления к нему в редакторе или в коде скрипта с отслеживанием скоростей (путем их запоминания в FixedUpdate)?

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.