ЧАВО по матрицам и кватернионам (4 стр)
Автор: Тимур Ж
ТРАНСФОРМАЦИИ
Что такое матрица поворота?
Матрица поворота используется для поворота точек вокруг системы координат. В то время как отдельные точки привязываются к новым координатам, расстояния между ними не меняются.
Все вращения определяются через тригонометрические функции синус и косинус. Для двумерной системы координат, матрица вращения такая:
\(R = \begin{pmatrix} \cos A & -\sin A \\ \sin A & \cos A \end{pmatrix}\)
Если угол А равен 0, то на выходе получаем единичную матрицу:
\(I = \begin{pmatrix} 1 & 0 \cr 0 & 1 \cr \end{pmatrix}\)
Если угол А равен +90 градусов, то матрица такова:
\(M = \begin{pmatrix}0 & -1 \cr 1 & 0\end{pmatrix}\)
Если угол А равен -90 градусов, то матрица такова:
\(I = \begin{pmatrix}0 & 1 \cr -1 & 0 \cr \end{pmatrix}\)
Углы поворота, взятые с обратным знаком равны транспонированию матрицы.
Если матрица вращения умножена на транспонированную, в результате будет единичная матрица
Как матрицы вращения влияют на систему координат?
Матрицы вращения влияют на систему координат следующим образом:
По договоренности положительные углы создают вращение по часовой стрелке, если смотреть из центра координат в положительном направлении оси вращения.
Следуя этому правилу, в праворукой декартовой системе координат, мы увидим следующие три вида:
+----------------------------------------+ | | | X-axis Y-axis Z-axis | | | | | | ^ Y ^ Z Y ^ | | | | | | | | | | | | | | | | | | | | | | O----> Z O----> X X <----O | | | +----------------------------------------+
Так как вращение на положительный угол создает вращение по часовой стрелке, можно создать набор координат для каждого такого вращения. Для упрощения рассмотрим поворот на +90:
Для X-оси:
\((0, 1, 0) \to (0, 0, 1)\)
\((0, 0, 1) \to (0, -1, 0)\)
\((0, -1, 0) \to (0, 0, -1)\)
\((0, 0, -1) \to (0, 1, 0)\)
Можно упростить так:
X' = X
Y' = -Z
Z' = Y
В матрице это будет так:
\(R_x = \begin{pmatrix}1 & 0 & 0 \cr 0 & \cos A & -\sin A \cr 0 & \sin A & \cos A \end{pmatrix}\)
То же самое для Y-оси:
X' = Z
Y' = Y
Z' = -X
В матрице:
\(R_y = \begin{pmatrix}\cos A & 0 & \sin A \cr 0 & 1 & 0 \cr -\sin A & 0 & \cos A\end{pmatrix}\)
Z-ось:
X' = -Y
Y' = X
Z' = Z
В матрице:
\(R_z = \begin{pmatrix}\cos A & -\sin A & 0 \cr \sin A & \cos A & 0 \cr 0 & 0 & 1\end{pmatrix}\)
Это и есть те три базовые матрицы, которые используются в OpenGL.
Для матриц 4x4:
\(M = \begin{pmatrix} 1 & 0 & 0 & 0\cr 0 & \cos A & -\sin A & 0 \cr 0 & \sin A & \cos A & 0 \cr 0 & 0 & 0 & 1 \end{pmatrix}\)
\(M = \begin{pmatrix} \cos A & 0 & -\sin A & 0\cr 0 & 1 & 0 & 0 \cr \sin A & 0 & \cos A & 0 \cr 0 & 0 & 0 & 1 \end{pmatrix}\)
\(M = \begin{pmatrix} \cos A & -\sin A & 0 & 0\cr \sin A & \cos A & 0 & 0 \cr 0 & 0 & 1 & 0 \cr 0 & 0 & 0 & 1 \end{pmatrix}\)
Что такое углы Эйлера?
Углы Эйлера это имя, данное для набора углов поворота определённых
относительно трех осей \(X,Y,Z\)
Их можно определить в векторном виде \((x,y,z)\) и хранить в структуре VECTOR.
К примеру, набор из \((0,0,0)\) всегда дает единичную матрицу.
Если представить углы в таком виде, то:
\((90,0,0)\) вращение на +90 градусов по X.
\((0,90,0)\) вращение на +90 градусов по Y.
\((0,0,90)\) вращение на +90 градусов по Z.
Что такое рыск, качение и тангаж?
Рыск, качение и тангаж (yaw, pitch, roll) — это термины аэронавтики для поворота в эвклидовой системе координат (Углах Эйлера), относительно местной системы координат самолета.
Представьте, что вы в самолете и смотрите вперед
Ось Z соединяет хвост и нос самолета.
X от конца левого крыла к концу правого крыла.
Y указывает с земли в небо.
тангаж — поворот по Х, рыск — поворот по Y, качение — поворот по Z.
Как мне комбинировать матрицы поворота?
Матрицы поворота комбинируются при помощи матричного умножения. Так что порядок умножения очень важен.
Что такое фиксация оси?
Фиксация (gimbal lock) — проблема, появляющаяся при использовании углов Эйлера. Так как конечный поворот зависит от порядка умножения, может произойти так, что одна ось вращения отобразиться на другую ось. Из-за чего станет невозможно повернуть объект в нужной оси.
К примеру, если вращать объект в порядке Z,Y,X и повернуть по оси Y на 90 градусов. В этом случае, вращение по оси Z будет произведено первым, а значит, будет проведено, верно. По оси Y также корректно. Однако после вращения по Y, ось X будет вращаться по оси Z.
Так что любое вращение по X на самом деле будет вращать по Z. Единственное решение этой проблемы — использовать Кватернионы.
Как правильно комбинировать матрицы вращения?
На самом деле, нет «правильного пути». Однако для возможности предсказать результат, некоторая организация необходима. Это также нужно, при создании полноценной 3D матричной библиотеки.
Самый простой путь повернуть объект:
\(M = X*Y*Z\)
где M финальная матрица вращения, и X,Y,Z матрицы вращения по осям.
Они опишет поворот по оси Х (тангаж), затем по Y(рыск)
и в конце по Z(качение).
Однако со стороны наблюдателя порядок вращений будет обратным.
К примеру, если ты стоишь и поворачиваешься налево,
то все в поле твоего зрения движется вправо.
Однако кто-либо кто смотрит на тебя, скажет, что ты повернулся направо.
Так что вид из камеры нужно моделировать в таком порядке:
\(M = (-Z)*(-Y)*(-X)\)
Это обратная матрица вращения генерируется, если камера считается как еще один объект.
Как сгенерировать матрицу вращения из углов Эйлера?
На первый взгляд, самый явный способ создать матрицу вращения из углов Эйлера это создать каждую матрицу отдельно и умножить их друг на друга.
m3_rotx( mat_x, vec -> angle_x ); m3_roty( mat_y, vec -> angle_y ); m3_rotz( mat_z, vec -> angle_z ); m3_mult( mat_tmp, mat_z, mat_y ); m3_mult( mat_final, mat_tmp, mat_x );
Из них можно сделать отдельную функцию:
m3_fromeuler( MATRIX *mat_final, VECTOR3 *euler )
Однако этот способ очень затратный в терминах времени обработки.
Для матрицы 4х4 гарантированно 10 элементов будут равны 0, два равны 1,
и четыре оставшихся будут иметь какое-то значение,
больше 75% всех матричных операций пройдет впустую. И это не считая
настройку и инициализацию каждой матрицы.
Вместе, больше 75% всех операций будут потрачены на нулевой или единичный результат.
Нужно более эффективное решение. К счастью, есть другой путь, определения
конечной матрицы.
Если все три матрицы записать в алгебраической форме,
то мы получим следующее выражение:
\(M = X*Y*Z\)
Где M - конечная матрица,
X - матрица вращения по X,
Y - матрица вращения по Y,
Z - матрица вращения по Z,
Расписав мы получим:
\(X = \begin{pmatrix}1 & 0 & 0\cr 0 & A & -B\cr 0 & B & A \end{pmatrix}\)
\(X = \begin{pmatrix}C & 0 & -D\cr 0 & 1 & 0\cr D & 0 & C \end{pmatrix}\)
\(X = \begin{pmatrix}E & -F & 0\cr F & E & 0\cr 0 & 0 & 1 \end{pmatrix}\)
где A, B косинус и синус вращения по оси X,
C, D косинус и синус вращения по оси Y,
E, F косинус и синус вращения по оси Z,
То выражение:
M = X.Y.Z
Можно разбить на два матричных умножения:
M' = X.Y M = M'.Z
Посчитаем сначала M':
M' = X.Y | 1 0 0 | | C 0 -D | M' = | 0 A -B | . | 0 1 0 | | 0 B A | | D 0 C | | 1.C + 0.0 + 0.D 1.0 + 0.1 + 0.0 1.-D + 0.0 + 0.C | M' = | 0.C + A.0 + -B.D 0.0 + A.1 + -B.0 0.-D + A.0 + -B.C | | 0.C + B.0 + A.D 0.0 + B.1 + A.0 0.-D + B.0 + A.C |
Упрощаем M':
| C 0 -D | M' = | -B.D A -B.C | | A.D B A.C |
Считаем M:
M = M'.Z | C 0 -D | | E -F 0 | M = | -BD A -BC | . | F E 0 | | AD B AC | | 0 0 1 | | C.E + 0.F + -D.0 C.-F + 0.E + -D.0 C.0 + 0.0 + -D.1 | M = | -BD.E + A.F + -BC.0 -BD.-F + A.E + -BC.0 -BD.0 + A.0 + -BC.1 | | AD.E + B.F + AC.0 AD.-F + B.E + AC.0 AD.0 + 0.0 + AC.1 |
Упрощаем M:
| CE -CF -D | M = | -BDE+AF -BDF+AE -BC | | ADE+BF -ADF+BE AC |
Конечная матрица вращения будет такой:
| CE -CF -D 0 | M = | -BDE+AF BDF+AE -BC 0 | | ADE+BF -ADF+BE AC 0 | | 0 0 0 1 |
Сначала считаются значения A,B,C,D,E и F. Также считаются значения
BD и AD так как они попадаются чаще одного раза.
Законченый алгоритм будет таким:
A = cos(angle_x); B = sin(angle_x); C = cos(angle_y); D = sin(angle_y); E = cos(angle_z); F = sin(angle_z); AD = A * D; BD = B * D; mat[0] = C * E; mat[1] = -C * F; mat[2] = -D; mat[4] = -BD * E + A * F; mat[5] = BD * F + A * E; mat[6] = -B * C; mat[8] = AD * E + B * F; mat[9] = -AD * F + B * E; mat[10]= A * C; mat[3] = mat[7] = mat[11] = mat[12] = mat[13] = mat[14] = 0; mat[15]= 1;
При использовании обычных матричных операций, операция заняла бы
128 умножений, 96 сложений и 80 присваиваний.
Оптимизированный алгоритм требует всего 12 умножений, 6 вычитаний
и 18 присваиваний.
Как видно при использовании оптимизированного алгоритма, мы получили
прирост производительности в 1000%.
Как мне перевести матрицу вращения в углы Эйлера?
Эта операция обратная к прошлому вопросу.
Дана матрица вращения:
| CE -CF -D 0 | M = | -BDE+AF BDF+AE -BC 0 | | ADE+BF -ADF+BE AC 0 | | 0 0 0 1 |
где A,B косинус и синус угла по оси X,
C,D косинус и синус угла по оси Y,
E,F косинус и синус угла по оси Z,
При использовании Сишной структуры для матрицы 4х4,
индексы элементов будут такими:
| 0 1 2 3 | M = | 4 5 6 7 | | 8 9 10 11 | | 12 13 14 15 |
Сравнивая эти таблицы, можно увидеть, что элемент [2] отвечает значению -D
или \(\sin(Y)\). Значит, вращение по Y можно рассчитать через арксинус.
Передав полученное значение в косинус, получим значение C.
Если C не равно 0, то вращение в X и Z, можно получить из третьей колонки
и первого ряда соответственно:
ось X: M[6] = -BC M[10] = AC ось Z: M[0] = CE M[1] = -CF
Углы могут быть получены из деления каждой пары значений на С и получения
ответа через арктангенс.
Если C равно 0, то эти вычисления невозможны. В этом случае поворот
по Y будет либо -90, либо 90. Так что D будет иметь значения либо 1, либо -1.
В этом случае мы получаем фиксацию оси. Поворот по оси X и Z будет
производиться на одной оси. Это можно увидеть из расчета осей поворота.
| 0.E -0.F 1 0 | M = | -B.1.E+AF B.1.F+AE -B.0 0 | | A.1.E+BF -A.1.F+BE A.0 0 | | 0 0 0 1 |
Умножение каждого из них дает:
| 0 0 1 0 | M = | -BE+AF BF+AE 0 0 | | AE+BF -AF+BE 0 0 | | 0 0 0 1 |
После перестановки:
| 0 0 1 0 | M = | -BE+AF AE+BF 0 0 | | AE+BF BE-AF 0 0 | | 0 0 0 1 |
Это можно увидеть из вида матрицы:
| 0 0 -1 0 | M = | -V W 0 0 | | W V 0 0 | | 0 0 0 1 |
Где V = BE-AF и W = AE+BF
Эти два значения отвечают за синус и косинус одной оси вращения.
Алгоритм будет таким:
angle_y = D = -asin( mat[2]); /* Считаем ось Y */ C = cos( angle_y ); angle_y *= RADIANS; if ( fabs( C ) > 0.005 ) /* ось зафиксирована? */ { trx = mat[10] / C; /* Нет, так что находим угол по X */ try = -mat[6] / C; angle_x = atan2( try, trx ) * RADIANS; trx = mat[0] / C; /* находим угол по оси Z */ try = -mat[1] / C; angle_z = atan2( try, trx ) * RADIANS; } else /* ось все-таки зафиксирована */ { angle_x = 0; /* Устанавливаем угол по оси X на 0 */ trx = mat[5]; /* И считаем ось Z */ try = mat[4]; angle_z = atan2( try, trx ) * RADIANS; } angle_x = clamp( angle_x, 0, 360 ); /* Приводим углы к диапазону */ angle_y = clamp( angle_y, 0, 360 ); angle_z = clamp( angle_z, 0, 360 );
Как мне сгенерировать матрицу вращения для выбранных осей и углов?
Есть лишь один способ создать этот тип матрицы поворота через математику кватернионов.
Смотри вопрос В54.
Как мне сгенерировать матрицу вращения, проецирующую один вектор на другой?
Когда создаешь анимационные программы, часто необходимо найти
матрицу поворота для проецирования направления одного вектора на другой.
Это проблему можно представить в виде двух векторов исходящих из одной точки.
То все поле значений вращения сформирует сферу.
В теории, есть бесконечное число осей вращения и углов, которыми можно
проецировать один вектор на другой. Все они лежат в плоскости
где все точки лежат на одинаковых расстояниях от обоих векторов.
Однако лишь одно решение имеет практический интерес. Это путь, который
будет иметь кротчайшее угловое расстояние между векторами.
Вращение оси по этому пути считается через векторное произведение
между двумя векторами:
V = Vs x Vf
Углы вращения считаются через скалярное умножение двух векторов:
-1 Vangle = cos ( Vs . Vf )
Одно из практических применений этого решения проблемы
- поиск кратчайшего воздушного пути между двумя городами.
В этом случае, каждый город представлен в виде вектора
направления в сферических координатах.
Так как Земля круглая, кратчайший путь будет иметь
кратчайшее угловое вращение между двумя городами.
Как мне использовать матрицы для перехода из одной системы координат в другую?
Похоже на предыдущую проблему, необходимо привести одну координатную систему к другой.
Однако, вместо того, чтобы пытаться проецировать одну координатную ось к другой,
необходимо совместить все три оси. Координатные системы представлены в виде матриц
3х3 или 4х4.
Проблема в том, чтобы найти матрицу поворота, которая бы отобразила одну матрицу на другую.
Математически это будет так:
Mfinal = Mrot . Morig
где Mfinal - конечная система координат,
Morig - оригинальная система координат
Mrot - искомая матрица вращения.
Значит, задача состоит в поиске матрицы Mrot. Это можно сделать
через перестановку в уравнении:
-1 Mfinal . Morig = Mrot -1 Mrot = Mfinal . Morig
Таким образом, нужную матрицу можно вычислить, умножив
обратную матрицу в оригинальной системе на финальную матрицу вращения.
Для проверки, представим, что и оригинальная и конечная матрица - единичные матрицы.
Тогда, матрица поворота должна совпадать с финальной и обратной к ней.
Единожды посчитанная, матрица вращения может быть превращена в кватернион.
Что такое матрица переноса?
Матрица переноса используется для установки объекта в 3D пространстве без его поворота.
Перенос может быть осуществлен с помощью матричного умножения
лишь при использовании 4х4 матриц.
Если перенос описан вектором [X Y Z], то матрица 4х4 будет такой:
| 1 0 0 X | | | | 0 1 0 Y | M = | | | 0 0 1 Z | | | | 0 0 0 1 |
Если вектор равен [0 0 0] то вершины останутся на своих местах.
Что такое матрица скалирования?
Матрица скалирования используется для расширения или
сжимания размеров 3D модели.
Если вектор скалирования равен [X Y Z] то матрица будет такой:
| X 0 0 0 | | | | 0 Y 0 0 | M = | | | 0 0 Z 0 | | | | 0 0 0 1 |
Если вектор скалирования равен [1 1 1], то в результате будет
единичная матрица и геометрия останется без изменений.
Что такое матрица сдвига?
Матрица сдвига используется для сдвига 3D модели в стороны.
К примеру, "курсивный" текс требует, чтобы каждый символ был сдвинут вправо.
В трех измерениях возможны 6 сторон сдвига:
o сдвигает X по Y
o сдвигает X по Z
o сдвигает Y по X
o сдвигает Y по Z
o сдвигает Z по X
o сдвигает Z по Y
Все 6 направлений сдвига могут быть собраны в одной матрице:
| 1 Syx Szx 0 | | | | Sxy 1 Szy 0 | M = | | | Sxz Syz 1 0 | | | | 0 0 0 1 | | |
Где Sij реализует сдвиг I по J
Так что, Sxy сдвигает X по Y
Теоретически, вращение в трех измерениях может быть комбинацией сдвига в 6 направлениях.
Как мне линейно интерполировать две матрицы?
Даны две матрицы, задача найти путь для определения средних точек
в зависимости от переменной t, которая изменяется в рамках от 0.0 до 1.0.
Это можно сделать переводом двух матриц либо в углы Эйлера, либо в сферические углы (кватернионы)
и вектор переноса. В этом случае каждая матрица превращается в пару 3D векторов.
Интерполировать между этими двумя векторами можно стандартной формулой линейной интерполяции:
Vr = Va + t .(Vb - Va )
где Vr - результат
Va - начальный вектор
Vb - конечный вектор
Это уравнение можно применять и на вектор переноса и на вектор поворота.
Однажды определенный, конечный перенос и поворот потом может быть переведен
обратно в среднюю матрицу.
Как мне сделать кубическую интерполяцию между четырьмя матрицами?
Дано четыре матрицы вращения или переноса, задача - найти способ определения
средний точек определённых параметром t.
Это можно сделать при помощи кубической интерполяции.
Как и с линейной интерполяцией четыре матрицы переводятся в соответствующие
вектора переноса и вращения (опять же, либо углы Эйлера, либо сферические углы)
Каждый из четырех векторов переводится в один вектор G. С помощью
математики сплайнов, этот вектор преобразуется в интерполированную матрицу M
Если вектор задан так:
| x1 x2 x3 x4 | G = | y1 y2 y3 y4 | | z1 z2 z3 z4 |
Умножение на начальную матрицу:
| -4.5 9.0 -5.5 1.0 | Mb = | 13.5 -22.5 9.0 0.0 | | -13.5 18.0 -4.5 0.0 | | 4.5 -4.5 1.0 0.0 |
создаст интерполированную матрицу Mi:
Mi = G .Mb
Это можно сделать через стандартную матрично-векторное умножение.
Интерполировать можно при помощи параметрической переменной t:
R = Mi . t |t^3| | xr | | A B C D | |t^2| | yr | = | E F G H | . |t | | zr | | I J K L | |1 |
Результат может быть преобразован обратно в матрицу вращ ения или переноса.
Необходимо заметить, что пути поворота могут оказаться закрученными,
это нормально, так как алгоритм пытается найти путь минимального вращения
между четырьмя векторами.
Из двух методов, сферические углы вращения дают обычно самую
лучшую интерполяцию для вращения.
Как отрисовать матрицу?
Когда используешь графическое окно для 3D анимации, бывает нужно видеть
как матрица вращения влияет на анимацию.
Однако, просто показ матрицы вращения в виде чисел не очень понятен.
Как альтернатива, можно отрисовать численные данные в виде графиков.
Похоже на графический эквалайзер в стерео, матрица вращения можно
представить в виде графика. Каждый элемент матрицы вращения отрисовывается в
виде отдельной колонки в рамке от -1 до +1.
Матрица 3х3 будет выглядеть так:
+--+ +--+ +--+ |##| | | | | +--+ +--+ +--+ | | | | | | +--+ +--+ +--+ +--+ +--+ +--+ | | |##| | | +--+ +--+ +--+ | | | | | | +--+ +--+ +--+ +--+ +--+ +--+ | | | | |##| +--+ +--+ +--+ | | | | | | +--+ +--+ +--+
В этом случае матрица вращения - единичная матрица,
где каждый элемент главной диагонали равен 1,
остальные равны 0.
Для удобства, отрицательные значения можно красить другим цветом,
нежели положительные.
#C++, #FAQ, #OpenGL, #кватернионы, #математика, #матрицы
9 марта 2011 (Обновление: 5 дек 2021)