Обоссал вектор, поклонился матрице
Это продолжение или расширение другой статьи, её лучше бы прочитать прямо сейчас, перед этой, чтоб хотя бы примерно понимать чо здесь происходит. Если с телефона читаешь — поверни его горизонтально, а то там формулы поехают. Алсо рекомендую на бумаге повторять все формулы. Там одна арифметика без спецзнаний, но её слишком много, чтобы разбираться в уме. 18+.
Напоминалка-1, точка — это вектор, набор координат. И ниже процедура вращения вектора (0, 1) вокруг начала координат в 2д.
Напоминалка-2 — любой вектор может быть представлен как сумма базисных векторов, умноженных на числа, ща это пригодится.
А теперь хояк, задачка: Пускай у меня в одном пространстве задано два базиса:
- ахуевший ([1, 0, 0], [0, 1, 0], [0, 0, 1])
- ебанутый, такой же как ахуевший, но повёрнутый вокруг оси Z (т.е. в плоскости XY)на угол a.
В ебанутом есть вектор [x, y, z], и мне зачем-то надо узнать его координаты в ахуевшем, назову их [xa, ya, za]. Идея такая: выясняю ахуевшие координаты каждого ебанутого базисного вектора, умножаю их на x y z соответсвенно, складываю. Получится [xa, ya, za] . Выяснить координаты каждого ебанутого вектора — одно и то же, что повернуть каждый ахуевший вектор вокруг оси Z на угол a (именно так получен ебанутый базис).
Первый на очереди [1, 0, 0], и на первой шпаргалке явно обозначено, что с ним происходит при повороте. Т.е. первый ебанутый вектор имеет координаты [cos(a), sin(a), 0]. Второй [0, 1, 0] аналогично, только с нюансом — это ведь тот же первый, только повёрнутый на 90°, значит всё точно так же, но с оффсетом угла на 90°. И по этой теме есть одна братская формула:
cos(a + 90) = -sin(a);
sin(a + 90) = cos(a).
Второй получился [-sin(a), cos(a), 0]. Третий вектор останется без изменений, т.к. совпадает с осью поворота: [0, 0, 1]. И вот ахуевше разложенный ебанутый базис:
[ cos(a), sin(a), 0]
[-sin(a), cos(a), 0]
[0, 0, 1]
Ну вот и всё, эти вектора по очереди домножаю на x y z и складываю:
[xa, ya, za] =
= x * [cos(a), sin(a), 0] + y * [-sin(a), cos(a), 0] + z * [0, 0, 1]
= [x * cos(a) - ye * sin(a), x * sin(a) + y * cos(a), 1]
Ты ведь ни хера не понял последнюю формулу, да? Давай глянем, чо происходит с каждой координатой отдельно. Умножение вектора на число происходит покоординатно, сложение векторов покоординатно и соответственно. Третье, нужное тебе, равенство следует из первых двух:
1. a * [x, y] = [a * x, a * y]
2. [x1, y1] + [x2, y2] = [x1 + x2, y1 + y2]
3. a * [x1, y1] + b * [x2, y2] = [a * x1 + b * x2, a * y1 + b * y2]
Тык вот. Ебанутый базис в ахуевшем назову EA, к его векторам буду обращаться через квадратные скобки и индекс: EA[0], EA[1], EA[2]. К координатам этих векторов через точку и имя: EA[0].x, EA[0].y, EA[0].z. Это чтоб не запутаться.
Итоговый вектор:
x * EA[0] + y * EA[1] + z * EA[2]
Отдельно координата x итогового:
xa = (x * EA[0]).x + (y * EA[1]).x + (z * EA[2]).x =
= x * EA[0].x + y * EA[1].x + z * EA[2].x
Если вдруг забыл: EA[0] = [cos(a), sin(a), 0], получается
xa = x * cos(a) + y * sin(a) + z * 0 = x * cos(a) + y * sin(a)
Остальные координаты аналогично.
Если когда-то видел умножение матриц, то ща словишь флешбек. Я заменил обращение к координате на такое же, как к вектору — через квадратные скобки и индекс. Итоговый вектор назову ea.
ea[0] = e[0] * EA[0][0] + e[1] * EA[1][0] + e[2] * EA[2][0]
ea[1] = e[0] * EA[0][1] + e[1] * EA[1][1] + e[2] * EA[2][1]
ea[2] = e[0] * EA[0][2] + e[1] * EA[1][2] + e[2] * EA[2][2]
Они на деревьях.
Это было умножение матрицы на вектор кароч. Получился перевод вектора из ебанутого базиса в ахуевший. А теперь давай глянем на это по-другому. Пускай вектор был взят до того, как базис стал ебанутым. Т.е. ахуевшие координаты известны до поворота и после. Значит он вращнулся прямо в ахуевшем пространстве, а ебанутый базис с синусами понадобился лишь для вычисления новых координат. Называть одноразовую хуйню базисом — много чести. Просто матрица преобразования. Опущенная.
Ещё раз вывод: та залупа с синусами и косинусами является матрицей вращения в плоскости XY, и чтоб чото повращать, надо это на неё умножить. Матрицы вращения вокруг оставшихся осей можно вычислить так же, как я вычислял эту. Вот как всё в итоге выглядит:
Осторожно, есть говно: координатные сетки бывают левосторонние и правосторонние, разница окажется в знаках синуса. Не заморачивайся пока этой хуйнёй, просто предупредил.
[ cos(a), sin(a), 0]
Mz = [-sin(a), cos(a), 0]
[0, 0, 1] [cos(a), 0, -sin(a)]
My = [0, 1, 0]
[sin(a), 0, cos(a)] [1, 0, 0]
Mx = [0, cos(a), sin(a)]
[0, -sin(a), cos(a)]
Настоятельно рекомендую всё это сделать самостоятельно на бумаге, может это и очень сложно, но точно реально, я проверял. Мозги качнутся так, что охуеешь заживо.
Задачка с вращением была просто примером, абстрагируйся от неё и вынеси только формулу умножения вектора на матрицу. Без синусов и говна.
Теперь я хочу вращать не один ссаненький вектор, а аж ёбаный базис целиком, все его нахер вектора одновременно. Пускай есть базис E и матрица поворота M, итоговый базис назову I. Обращаться к векторам и координатам буду так же, как в первый раз: к вектору через квадратные скобки и индекс, к координате через точку и название.
С первым вектором происходит то ж самое, как если бы это был просто вектор (потому что это просто вектор лол):
I[0].x = (E[0].x * M[0].x) + (E[0].y * M[1].x) + (E[0].z * M[2].x)
I[0].y = (E[0].x * M[0].y) + (E[0].y * M[1].y) + (E[0].z * M[2].y)
I[0].z = (E[0].x * M[0].z) + (E[0].z * M[1].z) + (E[0].z * M[2].z)
Блять, да и со вторым так же, он ж не особенный ни хуя
I[1].x = (E[1].x * M[0].x) + (E[1].y * M[1].x) + (E[1].z * M[2].x)
I[1].y = (E[1].x * M[0].y) + (E[1].y * M[1].y) + (E[1].z * M[2].y)
I[1].z = (E[1].x * M[0].z) + (E[1].z * M[1].z) + (E[1].z * M[2].z)
Ну этого уже достаточно для понимания алгоритма. Довай зоресую.
Раньше мне думалось, что всю эту залупу придумал какой-то поехавший под грибами. Но нет, я только что от одного лишь определения вектора логически пришёл к этому умножению. Перед финалом кратко повторю логическую цепочку.
- Вектор — набор чисел, можно складывать с другим вектором и умножать на число.
- Любой вектор можно разложить на сумму векторов, каждый из которых нельзя разложить на сумму остальных, они типа перпендикулярны друг другу: [x, y] = x * [1, 0] + y * [0, 1]. Систему таких векторов обозвали базисом. Алсо систему векторов удобно писать в столбик и называть матрицей.
- Чтобы совершить преобразование вектора (поворот, перемещение, хуещение), можно совершить преобразование его базисных векторов. Кайф в том, что его локальные координаты не изменятся.
- глобальные координаты после преобразования можно найти таким макаром: x * E[0] + y * E[1] + z * E[2], где x, y, z — локальные координаты, Е — базис, E[0], E[1], E[2] — вектора базиса
- x * E[0].a + y * E[1].a + z * E[2].a — формула из п.4 для каждой отдельной координаты. Вместо a можно подставить x y или z. Это обозвали умножением вектора на матрицу.
- Каждый вектор базиса можно умножить на одну и ту же матрицу, тогда преобразуется весь базис. И это получается умножение матрицы на матрицу. Формула вычисления каждого вектора в п.4, и каждой координаты в п.5.
Некоторые из этих пунктов подробно расписаны в предыдущей статье, остальные здесь. Я особенно кайфанул от того, что всё это высосано из одного лишь первого пункта, не использовано никаких операций кроме сложения и умножения, не притянуто никаких знаний извне. Сухой анализ набора чисел с двумя операциями, божественно нахой.
Из такого психованного перемножения следует больная тема — количество строк левой матрицы должно быть равно количеству столбов правой. Ну с этим можно бороться так же, как с неодинаковыми векторами: там можно прибавлять нули в конец, здесь тоже, но с прибавлением нуля вправо, нужно добавлять ещё один базисный вектор снизу, которому этот ноль соответствует. Пример:
[a, b] [a, b, 0]
[c, d] = [c, d, 0]
[0, 0, 1]
Кароч везде нули кроме главной диагонали, на ней единицы.