Может ли кто-нибудь объяснить (причины) последствия использования colum vs row major при умножении /конкатенации?

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

То, что я не понимаю, это то, что означает только «условное соглашение» - из статей here и здесь авторы утверждают, что не делают разница в том, как матрица хранится или переносится на GPU, но на второй странице эта матрица явно не эквивалентна тому, как она будет выложена в памяти для строки; и если я посмотрю на заполненную матрицу в своей программе, я вижу компоненты перевода, занимающие 4, 8 и 12 элементы.

Учитывая, что:

  

"пост-умножение с основными матрицами столбцов дает тот же результат   как предварительное умножение на основные матрицы.   «

Почему в следующем фрагменте кода:

        Matrix4 r = t3 * t2 * t1;
        Matrix4 r2 = t1.Transpose() * t2.Transpose() * t3.Transpose();

Имеет ли r! = r2 и почему pos3! = pos для :

        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) , который должен привести к тому, что вектор обрабатывается как основной файл , поэтому как может работать основная матрица столбца, предоставляемая моей математической библиотекой?

10 голосов | спросил sebf 23 +04002011-10-23T19:59:13+04:00312011bEurope/MoscowSun, 23 Oct 2011 19:59:13 +0400 2011, 19:59:13

4 ответа


10
  

Если я посмотрю на заполненную матрицу в своей программе, я вижу компоненты перевода, занимающие 4, 8 и 12 элементы.

Прежде чем начать, важно понять: это означает, что ваши матрицы row major . Поэтому вы отвечаете на этот вопрос:

  

моя основная матрица WVP столбца используется для преобразования вершин с вызовом HLSL: mul (вектор, матрица), который должен привести к тому, что вектор обрабатывается как старший, поэтому как матрица основных столбцов, предоставленная моей математической библиотекой работать?

довольно просто: ваши матрицы имеют большой размер.

Так много людей используют строковые или транспонированные матрицы, что они забывают, что матрицы не ориентированы естественно таким образом. Поэтому они видят матрицу трансляции следующим образом:

1 0 0 0
0 1 0 0
0 0 1 0
x y z 1

Это транспонированная матрица перевода . Это не так, как выглядит обычная матрица перевода. Перевод идет в четвертом столбце , а не в четвертой строке. Иногда вы даже видите это в учебниках, которые представляют собой полный мусор.

Легко узнать, является ли матрица в массиве строкой или столбцом. Если это строка, то перевод сохраняется в 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. Итак, если мы возьмем матрицу переводов proper и сохраним ее как майора столбца, она будет такой же, как перенос этой матрицы и ее сохранение в виде строки.

Это означает, что это всего лишь условное обозначение. Есть действительно два набора условностей: память и транспонирование. Хранение памяти - это столбец vs row major, а транспонирование - нормальное или транспонированное.

Если у вас есть матрица, которая была сгенерирована в строчном порядке, вы можете получить тот же эффект, перенести эквивалент столбца на основную матрицу. И наоборот.

Матричное умножение может быть выполнено только одним способом: при заданных двух матрицах в определенном порядке вы умножаете некоторые значения вместе и сохраняете результаты. Теперь 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. Учитывая это, для любой матрицы 4x4 A, A*C представляет матрицу, умножающую A на вектор-столбец x. И R*A представляет матрицу, умножающую вектор-строку x на.

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

Однако они связаны друг с другом. Это правда, что R != C. Однако также верно, что R = CT , где T - операция транспонирования. Две матрицы переносятся друг на друга.

Вот забавный факт. Так как векторы рассматриваются как матрицы, у них тоже есть вопрос с вопросом о столбцах и рядах. Проблема в том, что они оба выглядят одинаково . Массив поплавков одинаковый, поэтому вы не можете отличить 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 для

Потому что это не имеет смысла. Единственный способ для A * t == AT * t быть истинным, если бы A == AT . И это справедливо только для симметричных матриц.

ответил Nicol Bolas 24 +04002011-10-24T08:13:57+04:00312011bEurope/MoscowMon, 24 Oct 2011 08:13:57 +0400 2011, 08:13:57
3

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

Другой вопрос: сохраняете ли вы матрицы в памяти в порядке порядка строк или столбцов. Обратите внимание, что «row-major» и «major-column» - это not правильные термины для обсуждения конвенции строки-вектора /столбца-вектора ... хотя многие люди неправильно используют их как таковые. Макетная диаграмма майора и столбца также отличаются транспозицией.

OpenGL использует условное обозначение вектора столбца и порядок хранения столбцов, а D3D использует соглашение о векторе строки и порядок хранения строки (как минимум, D3DX, математическая библиотека), поэтому два транспонируют отменять и получается, что тот же макет памяти работает как для OpenGL, так и для D3D. То есть один и тот же список из 16 поплавков, сохраненных последовательно в памяти, будет работать одинаково в обоих API.

Это может означать, что люди говорят, что «не имеет никакого значения, как матрица хранится или переносится на GPU».

Что касается ваших фрагментов кода, r! = r2, потому что правило для транспонирования произведения (ABC) ^ T = C ^ T B ^ T A ^ T. Транспонирование распределяется по умножению с изменением порядка. Поэтому в вашем случае вы должны получить r.Transpose () == r2, а не r == r2.

Аналогично, pos! = pos3, потому что вы транспонировали, но не изменили порядок умножения. Вы должны получить wpvM * localPos == localPos * wvpM.Tranpose (). Вектор автоматически интерпретируется как вектор строки при умножении на левую часть матрицы и в качестве вектора столбца при умножении на правую часть матрицы. Помимо этого, нет никаких изменений в том, как выполняется умножение.

Наконец, re: «моя главная матрица WVP используется для преобразования вершин с вызовом HLSL: mul (вектор, матрица)», «Я не уверен в этом, но, возможно, путаница /ошибка привела к тому, что матрица выходят из уже перенесенной математической библиотеки.

ответил Nathan Reed 24 +04002011-10-24T00:50:17+04:00312011bEurope/MoscowMon, 24 Oct 2011 00:50:17 +0400 2011, 00:50:17
1

В 3D-графике вы используете матрицу для преобразования как вектора, так и точек. Учитывая тот факт, что вы говорите о матрице перевода, я буду говорить только о точках (вы не можете перевести вектор с матрицей или, говоря лучше, вы можете, но вы получите тот же вектор).

В умножении матрицы число столбцов первой матрицы должно быть равно числу строка второго (вы можете умножить матрицу тревоги на mxk).

Точка (или вектор) представлена ​​тремя компонентами (x, y, z) и может рассматриваться как строка или столбец:

  

colum (размер 3 X 1):

     

| х |

     

| у |

     

| г |

или

  

row (размер 1 X 3):

     

| х, у, г |

Вы можете выбрать предпочтительное соглашение, это просто соглашение. Назовем это матрицей преобразования. Если вы выберете первое соглашение, чтобы умножить точку p на матрицу, вам нужно использовать пост-умножение:

  

T * v (размерность 3x3 * 3x1)

иначе:

  

v * T (размерность 1x3 * 3x3)

     

авторы, по-видимому, утверждают, что не имеет значения, как   матрица сохраняется или переносится на графический процессор

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

  

p2 = B * A * p1; //первое соглашение

     

p3 = p1 * A * B; //второе соглашение

     

p2 == p3;

ответил Heisenbug 23 +04002011-10-23T22:12:48+04:00312011bEurope/MoscowSun, 23 Oct 2011 22:12:48 +0400 2011, 22:12:48
0

Проще говоря, причина разницы в том, что матричное умножение не является коммутативным . При регулярном умножении чисел, если A * B = C, то B * A также = C. Это не относится к матрицам. Вот почему вы выбираете основные черты или столбцы.

Почему вопрос не заключается в том, что в современном API (и я конкретно говорю о шейдерах здесь) вы можете выбрать свое собственное соглашение и умножить свои матрицы в правильном порядке для этого в вашем собственном шейдерном коде. API больше не применяет к вам тот или иной вариант.

ответил Maximus Minimus 24 +04002011-10-24T04:54:35+04:00312011bEurope/MoscowMon, 24 Oct 2011 04:54:35 +0400 2011, 04:54:35

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132