якщо я дивлюся на заселену матрицю в своїй програмі, я бачу компоненти перекладу, що займають 4-й, 8-й та 12-й елементи.
Перш ніж розпочати, важливо зрозуміти: це означає, що ваші матриці є основними рядками . Тому ви відповідаєте на це питання:
моя основна WVP-матриця стовпців успішно використовується для перетворення вершин за допомогою виклику HLSL: mul (вектор, матриця), що призводить до того, що вектор трактується як основний рядок, тож як може працювати основна матриця стовпців, що надається моєю математичною бібліотекою?
досить просто: ваші матриці є основними рядками.
Так багато людей використовують основні рядки або перекладені матриці, що вони забувають, що матриці не природно орієнтовані таким чином. Тому вони бачать матрицю перекладу таким:
1 0 0 0
0 1 0 0
0 0 1 0
x y z 1
Це транспонована матриця перекладу . Це не те, як виглядає нормальна матриця перекладу. Переклад йде в 4-му стовпчику , а не в четвертому рядку. Іноді ви навіть бачите це в підручниках, що зовсім сміття.
Неважко дізнатись, чи є матриця в масиві рядком чи стовпцем. Якщо він є головним рядком, то переклад зберігається в 3, 7 та 11-му індексах. Якщо це стовпець-головний, то переклад зберігається в 12, 13 та 14-му індексах. Нульові базові показники, звичайно.
Ваша плутанина випливає з того, що ви вважаєте, що ви використовуєте матриці основних стовпців, коли ви фактично використовуєте основні рядки.
Заява про те, що рядки проти головного стовпця є лише умовною умовою, є повністю правдивою. Механіка множення матриць та множення матриць / векторів однакова незалежно від умовності.
Те, що змінюється, означає зміст результатів.
Зрештою, матриця 4x4 - це лише сітка чисел 4x4. Він не повинен посилатися на зміну системи координат. Однак, як тільки ви присвоїли значення певній матриці, тепер вам потрібно знати, що зберігається в ній і як нею користуватися.
Візьміть матрицю перекладу, яку я вам показав вище. Це дійсна матриця. Ви можете зберігати матрицю float[16]
одним із двох способів:
float row_major_t[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1};
float column_major_t[16] = {1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1};
Однак я сказав, що ця матриця перекладу неправильна, оскільки переклад знаходиться в неправильному місці. Я спеціально сказав, що це транспонірується відносно стандартної конвенції щодо побудови матриць перекладу, яка повинна виглядати так:
1 0 0 x
0 1 0 y
0 0 1 z
0 0 0 1
Давайте розглянемо, як вони зберігаються:
float row_major[16] = {1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1};
float column_major[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1};
Зауважте, що column_major
це точно так само, як row_major_t
. Отже, якщо ми візьмемо належну матрицю перекладу і збережемо її як основний стовпець, це те саме, що транспонувати матрицю та зберігати її як основну рядок.
Це означає, що це лише нотаційна конвенція. Дійсно два набори конвенцій: зберігання пам'яті та транспозиція. Зберігання пам’яті є основним стовпцем проти рядка, тоді як транспозиція нормальна проти транспонірованої.
Якщо у вас є матриця, створена в порядку основного рядка, ви можете отримати той самий ефект, перемістивши еквівалент стовпця-основного в цій матриці. І навпаки.
Множення матриці можна зробити лише одним способом: задавши дві матриці, у визначеному порядку ви множите певні значення разом і зберігаєте результати. Тепер, A*B != B*A
але фактичний вихідний код для A*B
те саме, що і код для B*A
. Вони обидва використовують один і той же код для обчислення результатів.
Код множення матриці не має значення, чи зберігаються матриці в порядку основного стовпця або рядка-основного.
Те саме не можна сказати для множення вектор / матриця. І ось чому.
Множення вектор / матриця - хибність; цього зробити не можна. Однак ви можете помножити матрицю на іншу матрицю. Отже, якщо ви робите вигляд, що вектор є матрицею, ви можете ефективно робити множення на вектор / матрицю, просто виконавши множення матриці / матриці.
4D-вектор можна вважати стовпчиком-вектором або рядком-вектором. Тобто 4D-вектор можна розглядати як матрицю 4x1 (пам’ятайте: у позначеннях матриць підрахунок рядків приходить першим) або матрицю 1x4.
Але ось що: З огляду на дві матриці A і B, A*B
визначається лише у тому випадку, якщо кількість стовпців A збігається з кількістю рядків B. Тому, якщо A - наша матриця 4x4, B повинна бути матрицею з 4 рядками в цьому. Отже, ви не можете виконати A*x
, де x - вектор рядків . Аналогічно, ви не можете виконувати, x*A
де x - стовпець-вектор.
Через це більшість матричних математичних бібліотек роблять це припущення: якщо ви множите векторний раз на матрицю, ви дійсно маєте на увазі зробити множення, яке насправді працює , а не те, яке не має сенсу.
Давайте визначимо для будь-якого 4D вектора x наступне. C
має бути матричною формою стовпців-векторних x
та R
має бути матричною формою векторних рядків x
. З огляду на це, для будь-якої матриці 4х4 А A*C
являє собою матрицю, що множує A на стовпець-вектор x
. І R*A
являє матрицю, помножуючи вектор-рядок x
на A.
Але якщо ми подивимось на це, використовуючи сувору матричну математику, то побачимо, що вони не є рівнозначними . R*A
не може бути таким же, як A*C
. Це тому, що вектор-рядок - це не те саме, що вектор-стовпчик. Вони не одна і та ж матриця, тому вони не дають однакових результатів.
Однак вони пов’язані між собою. Це правда, що R != C
. Однак вірно також, що де T - операція транспонування. Дві матриці переносяться одна в одну.R = CT
Ось дивний факт. Оскільки вектори розглядаються як матриці, у них теж є стовпець проти головного рядка. Проблема в тому, що вони обидва виглядають однаково . Масив поплавків однаковий, тому ви не можете визначити різницю між R і C, просто подивившись на дані. Тільки спосіб відрізнити це від того, як вони використовуються.
Якщо у вас є дві матриці A і B, а A зберігається як основні рядки, а B як стовпчики, множення їх абсолютно безглуздо . Ви отримуєте дурниці в результаті. Ну не дуже. Математично те, що ви отримуєте, є еквівалентом занять . Або ; вони математично однакові.AT*B
A*BT
Отже, множення матриці має сенс лише в тому випадку, якщо дві матриці (і пам’ятайте: множення вектор / матриця - це просто матричне множення) зберігаються в одному і тому ж великому порядку.
Отже, чи векторний стовпчик-мажор чи рядок-мажор? Це і те, і інше, як було сказано раніше. Він є основним стовпцем лише тоді, коли він використовується як матриця стовпців, і він є основним рядком, коли він використовується як матриця рядків.
Тому, якщо у вас є матриця A, яка є головною колоною, це x*A
означає ... нічого. Ну, знову ж таки, це означає , але це не те, що ви насправді хотіли. Точно так само робить перенесене множення, якщо воно є основним рядком.x*AT
A*x
A
Таким чином, порядок вектора / матричного множення робить зміни, в залежності від вашого основного впорядкування даних (і ви використовуєте транспонований матриці).
Чому в наступному фрагменті коду робиться r! = R2
Тому що ваш код зламаний і баггі. Математично . Якщо цього результату ви не отримаєте, то або ваш тест на рівність невірний (питання точності з плаваючою комою), або ваш код множення матриці порушений.A * (B * C) == (CT * BT) * AT
чому pos3! = pos for
Тому що це не має сенсу. Єдиним способом бути правдою було б, якби . І це справедливо лише для симетричних матриць.A * t == AT * t
A == AT