Я пытаюсь научиться создавать матрицы представлений и проекций и продолжаю сталкиваться с трудностями в моей реализации из-за путаницы в двух стандартах на матрицы.
Я знаю, как умножить матрицу, и я вижу, что транспонирование перед умножением полностью изменило бы результат, поэтому необходимо умножать в другом порядке.
То , что я не понимаю , хотя это , Что означало только «нотационная конвенция» - из статей здесь и здесь авторы , по всей видимости утверждают , что это не делает никакой разницы в том , как матрица хранится или передается на GPU, но на втором страница этой матрицы явно не эквивалентна тому, как она будет размещена в памяти для строки-мажора; и если я смотрю на заселенную матрице в моей программе я вижу компоненты перевода , занимающие 4 - й, 8 - й и 12 - й элементы.
Учитывая это:
«постмножение с матрицами основных столбцов дает тот же результат, что и предварительное умножение с матрицами основных строк».
Почему в следующем фрагменте кода:
Matrix4 r = t3 * t2 * t1;
Matrix4 r2 = t1.Transpose() * t2.Transpose() * t3.Transpose();
Имеет ли г = г2! И почему POS3 = поз для! :
Vector4 pos = wvpM * new Vector4(0f, 15f, 15f, 1);
Vector4 pos3 = wvpM.Transpose() * new Vector4(0f, 15f, 15f, 1);
Меняется ли процесс умножения в зависимости от того, являются ли матрицы мажорами строк или столбцов , или это просто порядок (для эквивалентного эффекта?)
Одна вещь, которая не помогает этому стать более понятной, это то, что при предоставлении DirectX основная WVP-матрица моего столбца успешно используется для преобразования вершин с помощью вызова HLSL: mul (vector, matrix), что должно привести к тому, что вектор будет обрабатываться как row-major , так как же может работать матрица основных столбцов, предоставляемая моей математической библиотекой?
источник
Ответы:
Прежде чем я начну, важно понять: это означает, что ваши матрицы являются основными рядами . Поэтому вы отвечаете на этот вопрос:
это довольно просто: ваши матрицы являются мажорными строками.
Так много людей используют мажорные строки или транспонированные матрицы, что забывают, что матрицы не ориентированы таким образом естественным образом. Таким образом, они видят матрицу перевода следующим образом:
Это транспонированная матрица перевода . Это не то , что нормальный перевод матрица выглядит. Перевод идет в 4-й столбце , а не четвертая строка. Иногда, вы даже можете увидеть это в учебниках, что это полная фигня.
Легко узнать, является ли матрица в массиве строкой или столбцом. Если это главная строка, то перевод сохраняется в 3, 7 и 11-м индексах. Если это главный столбец, то перевод сохраняется в 12, 13 и 14-м индексах. Нулевые базовые показатели конечно.
Ваше заблуждение связано с убеждением, что вы используете матрицы с основными столбцами, тогда как на самом деле вы используете матрицы с основными рядами.
Утверждение, что столбец строки по сравнению с основным является Notational конвенции только совсем верно. Механика умножения матриц и умножения матриц / векторов одинакова независимо от соглашения.
Какие изменения смысл результатов.
Матрица 4х4 - это всего лишь сетка чисел 4х4. Это не обязательно относится к изменению системы координат. Однако после того, как вы присвоите значение определенной матрице, вам теперь нужно знать, что в ней хранится и как ее использовать.
Возьмите матрицу перевода, которую я показал вам выше. Это действительная матрица. Вы можете хранить эту матрицу в
float[16]
в одном из двух способов:Тем не менее, я сказал, что этот перевод матрица является неправильным, так как перевод в неправильном месте. Я специально сказал, что оно транспонировано относительно стандартного соглашения о том, как строить матрицы перевода, которое должно выглядеть следующим образом:
Давайте посмотрим на то, как они хранятся:
Обратите внимание, что
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 - вектор-столбец.Из-за этого большинство математических математических библиотек делают это предположение: если вы умножаете вектор на матрицу, вы действительно хотите выполнить умножение, которое действительно работает , а не то, которое не имеет смысла.
Определим для любого четырехмерного вектора x следующее.
C
должна быть матричной векторной формой столбцаx
иR
должна быть матричной векторной формой строкиx
. Учитывая это, для любой матрицы 4x4 AA*C
представляет матрицу, умножающую A на вектор-столбецx
. ИR*A
представляет матрицу, умножающую вектор-строкуx
на А.Но если мы посмотрим на это с использованием строгой математической математики, мы увидим, что они не эквивалентны .
R*A
не может быть таким же, какA*C
. Это потому, что вектор-строка - это не то же самое, что вектор-столбец. Они не одна и та же матрица, поэтому они не дают одинаковых результатов.Тем не менее, они связаны друг с другом. Это правда
R != C
. Однако верно также и то , где T - операция транспонирования. Две матрицы являются транспонированными друг для друга.R = CT
Вот забавный факт. Так как векторы рассматриваются как матрицы, они тоже имеют столбец против ряда-главного вопроса для хранения. Проблема заключается в том, что они оба выглядят одинаково . Массив с плавающей точкой одинаков, поэтому вы не можете определить разницу между R и C, просто взглянув на данные. Только способ отличить это от того, как они используются.
Если у вас есть какая - либо две матрицы А и В, и А хранятся в виде строки-основная и B в качестве столбцов, умножая их является совершенно бессмысленным . Вы получаете ерунду как результат. Ну не совсем. Математически, что вы получаете эквивалент делать . Или ; они математически идентичны.
AT*B
A*BT
Следовательно, умножение матриц имеет смысл, только если две матрицы (и помните: умножение вектора / матрицы - это просто умножение матриц) хранятся в одном и том же главном порядке.
Итак, является ли вектор-столбец мажор или мажор строки? Это не так, и ни один, как было сказано ранее. Это основной столбец, только когда он используется в качестве матрицы столбцов, и это основной ряд, когда он используется в качестве матрицы строк.
Поэтому, если у вас есть матрица A , которая не является столбец основных,
x*A
средств ... ничего. Ну, опять же , это значит , но это не то , что вы действительно хотели. Кроме того , это транспонированная умножение , если это строка-мажор.x*AT
A*x
A
Таким образом, порядок вектора / матричного умножения делает изменения, в зависимости от вашего основного упорядочения данных (и вы используете ли транспонированные матрицы).
Потому что ваш код сломан и глючит. Математически . Если вы не получите этот результат, то либо ваш тест равенства неправильно (вопросы точности с плавающей точкой) или ваш матричный код умножения нарушается.
A * (B * C) == (CT * BT) * AT
Потому что это не имеет смысла. Единственный способ быть правдой будет, если . И это верно только для симметричных матриц.
A * t == AT * t
A == AT
источник
Здесь работают два разных варианта соглашения. Во-первых, используете ли вы векторы строк или векторы столбцов, и матрицы для этих соглашений являются транспонированными друг для друга.
Другой вопрос заключается в том, храните ли вы матрицы в памяти в порядке основной строки или основной столбца. Обратите внимание, что «строка-майор» и «столбец-мажор» не являются правильными терминами для обсуждения соглашения о векторе-строке / векторе-столбце ... даже при том, что многие люди используют их как таковые. Макеты памяти основной строки и основной строки также различаются по транспонированию.
OpenGL использует соглашение о векторном столбце и порядок хранения основных столбцов, а D3D использует соглашение о векторе строк и основной порядок хранения строк (хорошо - по крайней мере, D3DX, математическая библиотека, делает), поэтому две транспонирования отменяются, и получается одна и та же схема памяти работает как для OpenGL, так и для D3D. То есть один и тот же список из 16 чисел, хранящихся последовательно в памяти, будет работать одинаково в обоих API.
Это может означать то, что люди говорят, что «неважно, как матрица хранится или передается в графический процессор».
Что касается фрагментов кода, r! = R2, потому что правило для транспонирования продукта: (ABC) ^ T = C ^ TB ^ TA ^ T. Транспозиция распределяется по умножению с рефералом порядка. Так что в вашем случае вы должны получить r.Transpose () == r2, а не r == r2.
Аналогично, pos! = Pos3, потому что вы транспонировали, но не меняли порядок умножения. Вы должны получить wpvM * localPos == localPos * wvpM.Tranpose (). Вектор автоматически интерпретируется как вектор строки при умножении на левой стороне матрицы, и как вектор столбца при умножении на правой стороне матрицы. Кроме этого, нет никаких изменений в том, как выполняется умножение.
И наконец: re: «Моя WVP-матрица столбца успешно используется для преобразования вершин с помощью вызова HLSL: mul (vector, matrix),« я не уверен в этом, но, возможно, возникла путаница / ошибка, из-за которой матрица вышла из математическая библиотека уже транспонирована.
источник
В трехмерной графике вы используете матрицу для преобразования как вектора, так и точек. Учитывая тот факт, что вы говорите о матрице перевода, я буду говорить только о точках (вы не можете перевести вектор с матрицей или, говоря лучше, вы можете получить тот же вектор).
В матричном умножении номер столбца первой матрицы должен быть равен номеру строки второй (вы можете умножить матрицу тревожности на mxk).
Точка (или вектор) представлена 3 компонентами (x, y, z) и может рассматриваться как строка или столбец:
или же
Вы можете выбрать предпочтительное соглашение, это просто соглашение. Давайте назовем это T матрицей перевода. Если вы выбираете первое соглашение, чтобы умножить точку p для матрицы, вам нужно использовать умножение после записи:
в противном случае:
Если вы используете всегда один и тот же конвенции, нет никакой разницы. Это не значит, что матрица другого соглашения будет иметь одно и то же представление в памяти, но при преобразовании точки с двумя различными соглашениями вы получите одну и ту же преобразованную точку:
источник
Я вижу, что компоненты перевода, занимающие 4-й, 8-й и 12-й элементы, означают, что ваши матрицы ошибочны.
Компоненты перевода всегда указываются в виде записей № 13, № 14 и № 15 матрицы преобразования ( считая самый первый элемент массива как элемент № 1 ).
Матрица преобразования основных строк выглядит следующим образом:
Колонна из основной матрицы преобразования выглядит следующим образом:
Роу основные матрицы указано спускаясь строки .
Объявление строки основную матрицу выше в виде линейного массива, я бы написать:
Это кажется очень естественным. Поскольку обратите внимание, на английском языке написано "row-major" - матрица появляется в тексте выше точно так же, как и в математике.
А вот и точка замешательства.
Основные матрицы столбцов указываются по столбцам
Это означает, что для определения матрицы преобразования основных столбцов в виде линейного массива в коде вам нужно написать:
Обратите внимание, что это совершенно нелогично! Основные матрицы столбцов имеют свои записи, указанные вниз по столбцам при инициализации линейного массива, поэтому первая строка
Определяет первый столбец матрицы:
и не первый ряд , как вы могли бы поверить в простом расположении текста. Вы должны мысленно транспонировать основную матрицу столбца, когда видите ее в коде, потому что первые 4 указанных элемента фактически описывают первый столбец. Я полагаю, именно поэтому многие люди предпочитают матрицы строк в коде (GO DIRECT3D !! кашель.)
Таким образом, компоненты перевода всегда имеют индексы линейного массива № 13, № 14 и № 15 (где первый элемент № 1), независимо от того, используете ли вы мажор строки или мажор столбца.
Что случилось с вашим кодом и почему он работает?
Что происходит в вашем коде, так это то, что у вас есть мажорная матрица столбца, но вы поместили компоненты перевода в неправильное место. Когда вы перемещаете матрицу, запись № 4 переходит к записи № 13, записи № 8 - № 13 и записи № 12 - № 15. И там у вас есть это.
источник
Проще говоря, причина разницы в том, что умножение матриц не является коммутативным . При регулярном умножении чисел, если A * B = C, то из этого следует, что B * A также = C. Это не относится к матрицам. Вот почему выбираются либо основные ряды, либо основные столбцы.
Почему это не имеет значения, так это то, что в современном API (а я специально говорю о шейдерах здесь) вы можете выбрать свое собственное соглашение и умножить матрицы в правильном порядке для этого соглашения в своем собственном шейдерном коде. API больше не навязывает вам одно или другое.
источник