Матрицы не очень сложны для понимания и использования. Более того, они нужны для написания быстрых преобразований и очень полезны для представления математических операций в компактной форме.
Матрица - это множество чисел, сгруппированных в колонки и столбцы. Здесь изображены две матрицы: Матрица А и Матрица В.
Матрица А - это матрица 2х3 (то есть у нее две строки и три столбца), тогда как матрица В - это матрица 3х3. Мы можем получить доступ к элементу матрицы А, используя запись А[m,n], где m - это строка, а n - столбец. Элемент в верхнем углу матрицы А будет обозначаться А[0,0] и он равен единице.
Вы можете производить большинство операций над матрицами так же, как Вы оперируете и с нормальными числами. Например, Вы можете их складывать или вычитать, соответственно складывая или вычитая каждый из компонентов.
Для примера, рассмотрим сложение двух матриц размерностью 2х3 - матрицы А и матрицы С:
При сложении матриц А и С нужно складывать каждый из элементов m, n. Суммы элементов займут в результирующей матрице соответствующие места:
Мы также можем умножить матрицу на скаляр k. Например, чтобы умножить матрицу А на 3, мы должны умножить на 3 каждый ее элемент.
Теперь поговорим об умножении двух матриц. Эта операция немного отличается от умножения на скалярную величину. Вы должны запомнить несколько правил:
Умножение матрицы m x n на матрицу n x r может быть описано алгоритмически следующим образом:
Для простоты посмотрите на рисунок:
Мы можем это сделать намного проще, написав программу на Си. Давайте определим матрицу 3х3 и напишем функцию, умножающую матрицы. Ниже показан исходный код:
// общая структура матрицы typedef struct matrix_typ { float elem[3][3]; // место для хранения матрицы } matrix, *matrix_ptr; void Mat_Mult3x3(matrix_ptr matrix_1, matrix_ptr matrix_2, matrix_ptr result) { index i, j, k; for(i=0; i < 3; j++) { for(j=0; j < 3; j++) { result[i][j] = 0; // инициализация элемента for(k = 0; k < 3; k++) { result->elem[i][j] += matrix_1->elem[i][k] * matrix_2->elem[k][j]; } // конец цикла по k } // конец цикла по j } // конец цикла по i } // конец функции
Прежде чем закончить говорить о матрицах, скажем еще об одной вещи: о единичной матрице. Не углубляясь в математические термины, я хочу сказать, что нам нужна такая матрица, умножая на которую мы получали бы исходную матрицу.
Говоря попросту, нам нужно иметь матрицу размерностью m x n, которую назовем матрицей I. Умножая на нее любую другую матрицу, мы должны получить исходную. Этой матрицей будет квадратная матрица, по главной диагонали которой записаны единицы, а все остальные элементы равны нулю:
Если мы умножим матрицу А на матрицу I,
то результатом будет исходная матрица А:
Прелесть матриц состоит в том, что Вы можете объединить все операции в одну матрицу, описывающую перемещение, масштабирование и вращение.
Помните, когда пишутся видеоигры, мы должны искать наиболее быстрый и эффективный алгоритм. Чем больше хитростей и трюков у нас в запасе, тем лучше. Теперь посмотрим на матрицы для перемещения, масштабирования и вращения.
Главной матрицей перемещений будем называть такую матрицу, в которой x_translation и y_translation - это коэффициенты перемещения объекта по осям Х и Y. Вот как она выглядит:
Главная матрица масштабирования - это такая матрица, в которой scale_x и scale_y - это коэффициенты масштабирования объекта по координатам х и y:
Такая матрица позволяет выполнять неоднородное масштабирование - мы можем задать один масштаб по оси Х и другой - по оси Y. Таким образом, если мы хотим масштабировать объект однородно, то должны задать scale_x = scale_y.
В главной матрице поворотов angle - это угол, на который Вы хотите повернуть объект:
Наступает торжественный момент. Теперь мы возьмем матрицы перемещения, масштабирования и поворота и перемножим их (получим конкатенацию), чтобы получить общую матрицу, реализующую все три функции сразу. Окончательно матрица будет выглядеть так:
Если Вы теперь умножите вершины объекта на эту матрицу, то получите перемещенный, повернутый и масштабированный объект. Не слабо, а?