NOTE
Заметка подразумевает, что читатель предварительно знаком с матрицами, углами Эйлера и кватернионами.
Матричная форма
Преимущества
- Вращение доступно сразу. Умножаем матрицу на вектор, и все.
- Матрицу вращения сразу можно подставить в графический API (так как они используют матрицы)
- Конкатенация. Матрицы можно перемножать, складывая цепочки вращений в одну матрицу.
Недостатки
- Накопление ошибок округления при многократных умножениях. Проблема называется Matrix creep, решается ортогонализацией
- Матричное представление неинтуитивно для человека.
- Матрица может быть неправильно сформирована. Не все матрицы могут описать ориентацию
Углы эйлера
Используемая конвенция Эйлера (порядок вращения): Z->X->Y
Преимущества
- Углы Эйлера просты понимании. Ими удобно пользоваться при отображении и вводе
- Наименьшее возможное представление. Углы Эйлера используют три числа для описания ориентации. Ни одна система не может параметризовать трехмерную ориентацию, используя менее трех чисел.
- Любой набор из трех чисел является действительным для выражения ориентации.
Недостатки
- Представления не являются уникальными.
- Интерполяция между двумя углами затруднена.
Подробнее об недостатках
Во-первых, есть проблема, что для конкретной ориентации существует множество различных троек углов Эйлера, которые можно использовать для описания этой ориентации. Это известно как aliasing и может быть существенной проблемой. Основные вопросы, такие как «представляют ли две тройки угла Эйлера одну и ту же ориентацию?» трудно ответить из-за aliasing.
Вторая и более проблемная форма aliasing происходит потому, что три угла не совсем независимы друг от друга. Например, поворот X = 135° — это то же самое, что и Y = 180°, затем X = 45°, затем Z = 180°.
Чтобы гарантировать уникальное представление для любой данной ориентации, мы должны ограничить диапазоны углов. Одной из распространенных техник является ограничение Y и Z до ±180° и ограничение X до ±90°. Это устанавливает соглашение «канонического» набора углов Эйлера.
Самый известный (и раздражающий) тип проблемы с aliasing, с которой страдают углы Эйлера, иллюстрируется этим примером: если мы вращаем Y на 45°, а затем вращаем X на 90°, это то же самое, что X на 90°, а затем Z на 45°. (Рисунок со сферой нарисовать) Фактически, как только выбираем X ±90° – в этот момент ось Z совпадает с осью Y, и начинают вращаться вокруг одной и той же вертикальной оси. Мы теряем одну степень свободы — два угла делают одно и то же, и управлять ориентацией независимо по трём осям больше невозможно. Это явление известно как Gimbal Lock. Чтобы устранить эту неоднозначность в каноническом наборе углов Эйлера, мы договариваемся: всё вращение вокруг вертикальной оси отдаётся углу Y, а Z принудительно обнуляется. Другими словами, в каноническом наборе, если X = ±90°, то Z = 0°, а Y остаётся таким, какой есть.
Операция интерполирования между двумя ориентациями A и B также является важной в задачах, например, анимаций персонажей или автоматического управления камерой.
Наивный подход чтобы проинтерполировать заключается в том, чтобы интерполировать каждый из трех углов независимо. Но это чревато некоторыми проблемами:
- Если не используются канонические углы Эйлера, то значение углов может быть очень большим.
- Второй тип проблемы связан с циклической природы углов вращения. Предположим, что есть два угла -170° и 170°. Эти два значения находятся всего на 20° друг от друга, но опять же, наивная интерполяция не будет вести себя правильно, вращая «длинный путь вокруг» на 340° вместо того, чтобы идти по более короткому пути на 20°. Решение заключается в том, чтобы идти по кратчайшей дуге:
В итоге, простые проблемы aliasing имеют обходные пути, но не gimbal lock. К сожалению, gimbal lock – это не просто мелкая неприятность, это фундаментальная проблема. Возможно, мы могли бы переформулировать наши вращения и разработать систему, которая не страдает от этих проблем? К сожалению, это невозможно. Это фундаментальное ограничение, связанное с использованием трёх чисел для описания 3D-ориентации. Любая система, которая параметризует ориентацию в трехмерном пространстве с помощью трех чисел, гарантированно будет иметь особенности в пространстве параметризации и, следовательно, будет подвержена таким проблемам, как gimbal lock.
Кватернионы
Небольшое знакомство
Кватернион — это гиперкомплексное число, которое определяет вращение объекта в пространстве. .
Мнимая часть представляет вектор, который определяет направление вращения. Вещественная часть определяет угол, на который будет совершено вращение. Его основное отличие от всем привычных углов Эйлера в том, что нам достаточно иметь один вектор, который будет определять направление вращения, чем три линейно независимых вектора, которые вращают объект в 3 подпространствах.
Рекомендую две статьи, в которых подробно рассказывается о кватернионах: Раз и Два
Теперь, когда у нас есть минимальные представления о кватернионах, давайте поймем, как вращать вектор.
Формула вращения вектора
, где
- — искомый вектор
- — исходный вектор
- — кватернион
- — обратный кватернион
Для начала, дадим понятие обратного кватерниона в ортонормированном базисе — это кватернион с противоположной по знаку мнимой частью.
Вычисление
Посчитаем и обозначим как :
Теперь выпишем отдельные компоненты и из этого произведения соберем новый кватернион:
Вычисление
Посчитаем оставшуюся часть, т.е. и получим искомый вектор.
Примечание: Чтобы не загромождать вычисления, приведем только мнимую (векторную) часть этого произведения. Ведь именно она характеризует искомый вектор.
Компоненты результирующего вектора
Реализация:
Vector3 QuanRotation(Vector3 v, Quaternion q)
{
float u0 = v.x * q.x + v.y * q.y + v.z * q.z;
float u1 = v.x * q.w - v.y * q.z + v.z * q.y;
float u2 = v.x * q.z + v.y * q.w - v.z * q.x;
float u3 = -v.x * q.y + v.y * q.x + v.z * q.w;
Quaternion M = new Quaternion(u1, u2, u3, u0);
Vector3 resultVector;
resultVector.x = q.w * M.x + q.x * M.w + q.y * M.z - q.z * M.y;
resultVector.y = q.w * M.y - q.x * M.z + q.y * M.w + q.z * M.x;
resultVector.z = q.w * M.z + q.x * M.y - q.y * M.x + q.z * M.w;
return resultVector;
}
Продолжение…
…