WWW.ROSSPROGRAMMPRODUCT.COM - Главная страница сайта

Статьи для программистов. Переводы.



Matrices can be your Friends.

By Steve Baker

Вольный перевод с английского. Публикуется с разрешения и одобрения автора.
Оригинал этой статьи вы можете найти на собственном сайте Стива Бейкера www.sjbaker.org по адресу: http://sjbaker.org/steve/omniv/matrices_can_be_your_friends.html

Подружитесь с матрицами.

Автор Стив Бейкер
Автор перевода - Вараксин А. Г.

Что больше всего сбивает с толку новичков в графическом программировании при знакомстве с матрицами - это то, что они выглядят как 16 совершенно произвольных, никак не связанных между собой чисел. Однако, небольшой ментальный эскиз, который я приведу, как мне кажется, поможет большинству людей уловить смысл матричных операций. Большинство программистов мыслят образами, и они не заботятся как следует о математических абстракциях.

Возьмём матрицу из OpenGL:

float m [ 16 ] ;

Будем считать, что это массив 4х4 с элементами, расположенными по четырём столбцам:

   m[0]  m[4]  m[ 8]  m[12]
   m[1]  m[5]  m[ 9]  m[13]
   m[2]  m[6]  m[10]  m[14]
   m[3]  m[7]  m[11]  m[15]

ПРЕДУПРЕЖДЕНИЕ: Математики хотели бы видеть их матрицы в более привычном формате (с индексными массивами, расположенными друг под другом). Будьте ВНИМАТЕЛЬНЫ с порядком матричных элементов!

...но мы-то OpenGL программисты, а не математики - верно?! Причина того, почему формат массивов OpenGL не совпадает с общепринятыми в математике, теряется в тумане времён. Однако, как мы увидим позже, это окажется счастливой случайностью.

Если вы имеете дело с матрицами, преобразовывающими только жесткие тела (т.е. без масштабирования, сдвига, сжатия и т. д.), тогда нижний ряд матрицы (3, 7, 11 и 15 элементы массива) всегда равны 0, 0, 0 и 1 соответственно, и, пока они сохраняют эти значения, мы можем без опаски забыть об их существовании.

Первые три элемента крайнего правого столбца матрицы означают просто полное смещение. Если вы вообразите некоторый небольшой компактный объект (вроде чайника), тогда 12, 13 и 14 элементы массива скажут вам, где находится этот объект в пространстве. Не имеет значения, в результате какой комбинации вращений и перемещений образовалась матрица, крайний правый столбец всегда сообщит вам местонахождение объекта. Очень удачно, что формат матриц в OpenGL именно такой, потому, что эти три элемента лежат в памяти последовательно.

Отлично, теперь нам осталось только узнать значение девяти, на первый взгляд, случайных элементов. Это верхние три элемента в каждом из первых трёх столбцов - и все вместе они определяют вращение объекта.

Наиболее лёгкий способ разобраться в этих числах - это определить, что происходит с четырьмя точками около начала координат после преобразования в матрицу:

   (0,1,0)
        |  /(0,0,1)
        | /
        |/___(1,0,0)
   (0,0,0)

Это четыре вершины куба 1х1х1 с углом в начале координат.

Где лежит куб после матричного преобразования?

Если пренебречь нижним рядом, тогда часть матрицы, отвечающая за вращение, просто описывает новые координаты вершин куба:

    (1,0,0)  --->  ( m[0], m[1], m[2] )
    (0,1,0)  --->  ( m[4], m[5], m[6] )
    (0,0,1)  --->  ( m[8], m[9], m[10])
    (0,0,0)  --->  ( 0, 0, 0 )

После этого вы просто прибавляете смещение к каждой точке, так, что:

    (1,0,0)  --->  ( m[0], m[1], m[2] ) + ( m[12], m[13], m[14] )
    (0,1,0)  --->  ( m[4], m[5], m[6] ) + ( m[12], m[13], m[14] )
    (0,0,1)  --->  ( m[8], m[9], m[10]) + ( m[12], m[13], m[14] )
    (0,0,0)  --->  ( 0, 0, 0 ) + ( m[12], m[13], m[14] )

Однажды узнав всё это, становится легко позиционировать объект там, где вы хотите, без возни с множественными вызовами функции glRotate.

Просто вообразите маленький кубик у начала координат - словно жестко присоединенный к вашей модели. Думайте о том, где кубик может находиться после перемещения модели - запишите координаты его вершин и вот вам готовая матрица.

Так, если я дам вам такую матрицу:

   0.707, -0.707, 0, 10
   0.707,  0.707, 0, 10
   0    ,  0    , 1,  0
   0    ,  0    , 0,  1

...вы могли бы легко видеть, что ось X этого маленького кубика расположена где-то между X и Y осями координат, Y-ось теперь где-то между Y - осью и отрицательным направлением оси X, а ось Z осталась там, где была. Сам кубик был перемещён на 10 единиц по осям X и Y. Это вращение на 45 градусов вокруг оси Z и перемещение на 10, 10, 0! Не нужно напрягаться с математикой - просто создайте в уме картинку с маленьким кубиком - и вам не надо заботиться о порядке вызова функций трансформации или других трудных операций подобных этим...

Набравшись опыта, вы можете обнаружить, что последний ряд чисел тоже воздействует на ваш маленький кубик.

Так, хотите ли вы узнать, как использовать матрицу для сжатия объектов, а также растяжения, сдвига, и т. д.? Просто вычислите, где находятся вершины вашего кубика после деформации, запишите их координаты - и матрица готова. Как будет выглядеть кубик, если сильный ветер задует со стороны —X - бесконечности?

  1, 0.3, 0, 0
  0, 0.9, 0, 0
  0, 0  , 1, 0
  0, 0  , 0, 1

Видите - Y-ось наклонилась вправо на 0.3 единицы, и кубик стал чуть-чуть ниже.

Предположим, персонаж вашего мультфильма собирается вертикально подпрыгнуть, и вы хотите его слегка сжать перед прыжком и растянуть во время прыжка. Тогда просто линейно интерполируйте эти две матрицы:

  1  , 0  , 0, 0            1  , 0  , 0, 0
  0  , 0.8, 0, 0            0  , 1.2, 0, 0
  0  , 0  , 1, 0   ===>     0  , 0  , 1, 0
  0  , 0  , 0, 1            0  , 0  , 0, 1

Неплохо - ваш герой становится то короче то длиннее - как насчёт того, чтобы сделать его также чуть толще (при сохранении объёма)?

  1.2, 0  , 0  , 0         0.9,0  , 0 , 0
  0  , 0.8, 0  , 0         0  ,1.2, 0 , 0
  0  , 0  , 1.2, 0 ===>    0  ,0  ,0.9, 0
  0  , 0  , 0  , 1         0  ,0  , 0 , 1

Сначала кубик стал меньше по оси Y и крупнее на осях X и Z, затем подрос вдоль оси Y и похудел по X/Z... - всё очень просто!

Не только гораздо легче представлять себе трансформации таким образом, но также и гораздо более эффективнее. Рассматривая все трансформации, как одну - единственную операцию на единичном кубе вы сэкономите массу времени на вызове glRotate/glTranslate/glScale - функций, каждая из которых совершает целый ряд умножений/сложений, связывающих новые трансформации с матрицей, лежащей наверху стека.

Наконец, вот ещё одна матрица, о существовании которой мы обязательно должны знать - это т. н. "Единичная" матрица:

  1, 0, 0, 0
  0, 1, 0, 0
  0, 0, 1, 0
  0, 0, 0, 1

Как вы можете видеть, матрица оставляет все оси "как есть" и не совершает никаких переносов.
Это "ничего-не-делающая" матрица.

Итак, разобраться в матрицах - это и в самом деле нетрудно, нужно просто заставить поработать своё воображение.


Другие статьи для программистов...




Copyright © 2007 г. РОССПРОГРАММПРОДУКТ ®.


Rambler's Top100