Путаница между порядком матрицы С++ и OpenGL (строка-майор против столбца-майора)
Я сильно запутался в определениях матриц. У меня есть матричный класс, который содержит a float[16]
, который, как я предполагал, является строковым, на основе следующих наблюдений:
float matrixA[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
float matrixB[4][4] = { { 0, 1, 2, 3 }, { 4, 5, 6, 7 }, { 8, 9, 10, 11 }, { 12, 13, 14, 15 } };
matrixA
и matrixB
оба имеют одинаковый линейный макет в памяти (т.е. все числа в порядке). В соответствии с http://en.wikipedia.org/wiki/Row-major_order это указывает на макет строки.
matrixA[0] == matrixB[0][0];
matrixA[3] == matrixB[0][3];
matrixA[4] == matrixB[1][0];
matrixA[7] == matrixB[1][3];
Следовательно, matrixB[0]
= строка 0, matrixB[1]
= строка 1 и т.д. Опять же, это указывает на макет строки.
Моя проблема/путаница возникает, когда я создаю матрицу перевода, которая выглядит так:
1, 0, 0, transX
0, 1, 0, transY
0, 0, 1, transZ
0, 0, 0, 1
который выложен в памяти как { 1, 0, 0, transX, 0, 1, 0, transY, 0, 0, 1, transZ, 0, 0, 0, 1 }
.
Затем, когда я вызываю glUniformMatrix4fv, мне нужно установить флаг транспонирования в GL_FALSE, указывая на то, что он имеет значение столбца, иначе преобразует, например, перевод/масштаб и т.д. t применяется правильно:
Если транспонирование GL_FALSE, предполагается, что каждая матрица представлена в главный заказ. Если транспонирование GL_TRUE, каждая матрица предполагается поставляться в основном порядке.
Почему моя матрица, которая, как представляется, имеет большое значение строки, должна быть передана OpenGL в качестве основного столбца?
Ответы
Ответ 1
матричная нотация, используемая в документации opengl, не описывает макет памяти для макетов OpenGL
Если подумать, что будет легче, если вы забудете/забудете о всей вещи "строка/столбец-майор". Это связано с тем, что помимо основной строки/столбца программист может также решить, как он захочет выложить матрицу в памяти (будь то соседние элементы образуют строки или столбцы), в дополнение к нотации, которая добавляет к путанице.
Матрицы OpenGL имеют тот же макет памяти, что и матрицы directx.
x.x x.y x.z 0
y.x y.y y.z 0
z.x z.y z.z 0
p.x p.y p.z 1
или
{ x.x x.y x.z 0 y.x y.y y.z 0 z.x z.y z.z 0 p.x p.y p.z 1 }
-
x, y, z - 3-компонентные векторы, описывающие матричную систему координат (локальная система координат в пределах относительно глобальной системы координат).
-
p - трехкомпонентный вектор, описывающий начало системы координат матрицы.
Это означает, что матрица перевода должна быть выложена в памяти следующим образом:
{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, transX, transY, transZ, 1 }.
Оставьте это, и остальное должно быть легко.
--- цитата из старого opengl faq -
9.005 Являются ли матрицы OpenGL столбцами или строковыми?
В целях программирования матрицы OpenGL представляют собой 16-значные массивы с базовыми векторами, которые последовательно расположены в памяти. Компоненты перевода занимают 13-й, 14-й и 15-й элементы 16-элементной матрицы, где индексы пронумерованы от 1 до 16, как описано в разделе 2.11.2 Спецификации OpenGL 2.1.
Колонка-майор и рядовая строка - это чисто условное обозначение. Обратите внимание, что пост-умножение с основными матрицами столбцов дает тот же результат, что и предварительное умножение на основные матрицы. Спецификация OpenGL и справочное руководство OpenGL используют нотацию столбцов. Вы можете использовать любые обозначения, если это четко указано.
К сожалению, использование формата столбцов в спецификации и синей книге привело к бесконечной путанице в сообществе разработчиков OpenGL. Обозначение столбца-майора предполагает, что матрицы не выкладываются в памяти, как ожидал программист.
Ответ 2
Подводя итог ответам SigTerm и dsharlet: обычным способом преобразования вектора в GLSL является умножение вектора на матрицу преобразования:
mat4 T; vec4 v; vec4 v_transformed;
v_transformed = T*v;
Чтобы это сработало, OpenGL ожидает, что макет памяти T
будет, как описано SigTerm,
{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, transX, transY, transZ, 1 }
который также называется "основным столбцом". Однако в вашем шейдерном коде (как указано вашими комментариями) вы умножили вектор на матрицу преобразования:
v_transformed = v*T;
который дает только правильный результат, если T
транспонирован, т.е. имеет макет
{ 1, 0, 0, transX, 0, 1, 0, transY, 0, 0, 1, transZ, 0, 0, 0, 1 }
(т.е. "строка майор" ). Поскольку вы уже предоставили правильный макет вашему шейдеру, а именно строку major, не нужно было устанавливать флаг transpose
glUniform4v
.
Ответ 3
Вы имеете дело с двумя отдельными проблемами.
Во-первых, ваши примеры касаются макета памяти. Ваш массив [4] [4] является основным, потому что вы использовали соглашение, установленное многомерными массивами C, чтобы соответствовать вашему линейному массиву.
Вторая проблема - это соглашение о том, как вы интерпретируете матрицы в своей программе. glUniformMatrix4fv используется для установки параметра шейдера. Независимо от того, рассчитано ли ваше преобразование для преобразования вектор строки или столбца, вопрос о том, как вы используете матрицу в своем шейдерном коде. Поскольку вы говорите, что вам нужно использовать столбчатые векторы, я предполагаю, что ваш шейдерный код использует матрицу A и вектор-столбец x для вычисления x '= A x.
Я бы сказал, что документация glUniformMatrix запутанна. Описание параметра транспонирования - это действительно окольный способ просто сказать, что матрица транспонирована или нет. Сам OpenGL переносит эти данные в ваш шейдер, независимо от того, хотите ли вы его транспонировать или нет, - это соглашение, которое вы должны установить для своей программы.
У этой ссылки есть хорошее обсуждение: http://steve.hollasch.net/cgindex/math/matrix/column-vec.html