Небольшие изыскания (извиняюсь за сумбур, писал заметку для себя, пока ковырялся, но решил выложить, вдруг кому-то будет полезно)
Произведение матриц
Матрицы умножаются строка на столбец (исторически так сложилось). Значит, ширина первой матрицы должна быть равна высоте второй матрицы.
А результирующая матрица имеет размеры (высота первой матрицы) x (ширина второй матрицы). Порядок умножения важен!
Для визуального запоминания):
|- - - -| |- - - -| |-| |-| |- - - -| * |- - - -| = |- - - -| |- - - -| * |-| = |-| |- - - -| |- - - -| |-| |-| |- - - -| |- - - -| |-| |-|
Хранение матриц в памяти
На бумаге матрица может выглядеть так:
|11 12 13 14| |21 22 23 24| |31 32 33 34| |41 42 43 44|
Однако в памяти есть только последовательность значений. И матрица может храниться двумя способами: "row-major order" и "column-major order".
Row-major order = обычный двумерный массив Си = по строкам: первая строка, вторая строка, ... = 11 12 13 14 21 22 23 24 ...
Column-major order = по столбцам: первый столбец, второй столбец,... = 11 21 31 41 12 22 32 42 ...
Матрица-столбец и матрица-строка в памяти выглядят абсолютно одинаково. Для транспонирования вектора не нужно делать ничего.
Для вектора Row-major order и Column-major order идентичны - есть просто 4 значения.
|-| |-| |- - - -| |-| |-|
|- - - -| |-| |-| |- - - -| * |-| = |-| |- - - -| |-| |-| |- - - -| |-| |-|
Матрица масштабирования * вектор = скалированный вектор.
Матрица вращения * (матрица масштабирования * вектор) = скалированный и повернутый вектор.
Матрица перемещения * (Матрица вращения * (матрица масштабирования * вектор)) = трансформированный вектор.
Скобки можно опустить:
Матрица перемещения * матрица вращения * матрица масштабирования * исходный вектор = трансформированный вектор.
Если предварительно перемножить матрицы:
матрица трансформаций * исходный вектор = трансформированный вектор.
В матрице трансформаций 4x4 последнняя строка (0 0 0 1) и заранее известна. Да и умножать на нули и единицу - лишняя трата времени.
Поэтому в движке матрицы трансформаций хранятся в виде Matrix3x4. А Матрицы 4x4 используются только при проецировании вектора.
Matrix3x4 * Vector3 = Vector3. Но это только для оптимизации вычислений.
С математической точки зрения умножать Matrix3x4 на Matrix3x1 вообще нельзя.
Было бы верным Matrix3x4 * Vector4 = Matrix3x4 * Matrix4x1 = Matrix3x1 = Vector3
Тут Vector4 это Vector3 с добавленной единицей в последнем элементе.
В OpenGL
матрицы хранятся ПО СТОБЦАМ "column-major order", а не по строкам. И это влияет на математику при умножении матриц в шейдере.
То есть OpenGL ожидает элементы матрицы в другом порядке, чем они хранятся в движке Urho3D. И умножение матриц в шейдере исходит их этого.
Фактически перед передачей в шейдер матрицы нужно транспонировать. Но это лишняя трата процессороного времени.
Матрицы из движка передаются блоком данным как есть,
а в шейдере эти матрицы считаются транспонированными (вектор при транспонировании в памяти вообще не меняется). Раз все данные
в шейдер передаются транспонированными, то меняется и порядок умножения.
Транспонированный вектор-столбец * транспонированная матрица трансформаций = транспонированный трансформированный вектор (который в памяти идентичен нетранспонированному)
|- - - -| |- - - -| * |- - - -| = |- - - -| |- - - -| |- - - -|
Исходный вектор тр * матр масштаб тр * матр вращ тр * матр перемещ тр = трансформированный вектор транспонированный
В отличии от движка Urho3D матрица трансформаций в шейдер передается как Matrix4x4 (uniform mat4 cModel), а не Matrix3x4.
Перед передачей восстанавливается последняя строка (0, 0, 0, 1).
void Graphics::SetShaderParameter(StringHash param, const Matrix3x4& matrix).
В DirectX
В директХ матрицы трансформаций Matrix3x4 передаются прямым копированием и хранятся в uniform float4x3 cModel
(т.е. в шейдере блок данных интерепретируется как транспонированная матрица).
flot4 * float4x3 = float3 (mat1x4 * mat4x3 = mat1x3) - работает нормально и математически верно
#define GetWorldPos(modelMatrix) mul(iPos, modelMatrix)
|- - -| |- - - -| * |- - -| = |- - -| |- - -| |- - -|
Вектор так же как и в OpenGL умножается на матрицу слева.
Исходный вектор * матр масштаб * матр вращ * матр перемещ = трансформированный вектор
Исходный вектор * матрица трансформаций = трансформированный вектор
Почему-то в интернете много инфы, что DirectX хранит матрицу по строкам,
но в https://msdn.microsoft.com/en-us/library/windows/desktop/bb509634(v=vs.85).aspx#Matrix_Ordering написано
Matrix packing order for uniform parameters is set to column-major by default.
Может какие-то старые версии использовали row-major order (или библиотечка DX (CPU) использовала его), а в HLSL (GPU) ожидается передача по столбцам.
Нужно больше газа веспен кода в качестве примера как на GL / так и на DX ) Ну и в целом - отличная заметка, думаю пригодиться ) След. наверное про написание простейшего шейдера ?
ну я заметку для себя писал, как простейший шейдер написать я и так знаю))
это понятно что знаешь, но на тот случай, если вдруг - забудешь ))
Полезная инфа, спасибо.
1vanK Для чего нужна координата w в системе координат x y z w? Почему у нас матрица 4x4 а не 4x3? Ведь мы бы могли хранить матрицу поворота 3x3 и ниже неё координаты xyz. Зачем нам 4x4?
у матрицы переноса дельта в четвертом столбце, матрицей 3x3 никак не обойтись
для матрицы проекции необходимые данные записываются в 4й строке, поэтому до 4x3 урезать не получится
ну а w - потому что https://ru.wikipedia.org/wiki/Однородные_координаты
а вообще так на пальцах не пояснить, тут надо углубляться в математику, откуда вообще берутся эти матрицы трансформаций
например http://pmg.org.ru/basic3d/index.html -> http://kappasoft.narod.ru/info/3d/3d.htm http://pmg.org.ru/basic3d/math.htm
Я то знаю ответ )) это на собеседованиях часто спрашивают.
извини, я не трудоустраиваюсь, чтоб ты меня экзаменовал, впредь подумаю, отвечать ли на твои вопросы xD
f1ufx_, по ходу устроил масштабное анкетирование, спросил о графике работы форумчан, даже не поленился пройтись по многим темам для поиска "жертвы")) Нелегко нынче работодателям...
Ох, ну тебе же придётся когда-то трудоустраиваться..
Говоря о матрицах, важно упомянуть Аффинные преобразования - это такие, при которых параллельные линии остаются параллельными, перпендикулярные - перпендикулярными. Записывается оно как то так:
Чтобы записать это в матричном виде, матрицу-строку [x,y] нужно умножить на матрицу столбец [a,b,c]. Но количество элементов должно быть одинаково. Поэтому матрицу-строку добили единицей [x,y,1].
а может это просто привычка ) вдруг он большой начальник )
> Ох, ну тебе же придётся когда-то трудоустраиваться..
ну может и придется, но с моей точки зрения это выглядит, что я хочу помочь чем могу, а меня троллят)
Тема в архиве.