Подтвердить что ты не робот

Путаница между порядком матрицы С++ и 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 в качестве основного столбца?

4b9b3361

Ответ 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