C ++和OpenGL矩阵顺序之间的混淆(行主要与列主要)

时间:2013-07-18 07:46:54

标签: c++ math opengl matrix

我对矩阵定义感到非常困惑。我有一个矩阵类,它根据以下观察结果保存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 } };

matrixAmatrixB在内存中都具有相同的线性布局(即所有数字都按顺序排列)。根据{{​​3}},这表示行主要布局。

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 }

然后当我调用http://en.wikipedia.org/wiki/Row-major_order时,我需要将转置标志设置为GL_FALSE,表示它是列主要的,否则转换如翻译/缩放等不能正确应用:

  

如果转置是GL_FALSE,则假定每个矩阵都被提供   专栏主要订单。如果转置是GL_TRUE,则假设每个矩阵   按行主要订单提供。

为什么我的矩阵看起来是行主要的,需要作为列主要传递给OpenGL?

4 个答案:

答案 0 :(得分:61)

opengl文档中使用的

矩阵表示法没有描述OpenGL矩阵的内存中布局

如果你认为放弃/忘记整个“行/列专业”的事情会更容易。这是因为除了行/列专业之外,程序员还可以决定他如何在内存中布置矩阵(相邻元素是否形成行或列),以及符号,这会增加混乱。

OpenGL矩阵有same memory layout as directx matrices

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是描述矩阵坐标系原点的3分量向量。

这意味着翻译矩阵应该像这样在内存中布局:

{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, transX, transY, transZ, 1 }.

保持原样,其余的应该很容易。

---来自旧的opengl faq引用 -


  

9.005 OpenGL矩阵是专列还是行专业?

     

出于编程目的,OpenGL矩阵是16值数组,其基本向量在内存中连续排列。转换组件占据16元素矩阵的第13,14和15个元素,其中索引的编号从1到16,如OpenGL 2.1规范的2.11.2节所述。

     

列主要与行主要纯粹是一种符号约定。请注意,使用列主矩阵进行后乘法会产生与使用行主矩阵预乘的结果相同的结果。 OpenGL规范和OpenGL参考手册都使用列主要表示法。只要明确说明,您就可以使用任何符号。

     

可悲的是,在规范和蓝皮书中使用列主格式导致了OpenGL编程社区的无休止的混乱。列主要符号表明矩阵不像程序员所期望的那样在内存中布局。


答案 1 :(得分:49)

总结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 }

(即' row major')。由于您已经为着色器提供了正确的布局,即行专业,因此无需设置transpose的{​​{1}}标志。

答案 2 :(得分:14)

您正在处理两个不同的问题。

首先,您的示例正在处理内存布局。你的[4] [4]数组是行主要的,因为你已经使用了由C多维数组建立的约定来匹配你的线性数组。

第二个问题是关于如何解释程序中的矩阵的约定问题。 glUniformMatrix4fv 用于设置着色器参数。是否针对行向量列向量变换计算变换是您在着色器代码中如何使用矩阵的问题。因为你说你需要使用列向量,我假设你的着色器代码使用矩阵 A 和列向量 x 来计算 x' = A x

我认为 glUniformMatrix 的文档令人困惑。转置参数的描述是一种非常迂回的方式,只是说矩阵是转置的或不是。 OpenGL本身就是将这些数据传输到着色器,无论你是否要转置它都是你应该为你的程序设定的惯例。

此链接有一些很好的进一步讨论:http://steve.hollasch.net/cgindex/math/matrix/column-vec.html

答案 3 :(得分:0)

我认为这里现有的答案非常没有帮助,我可以从评论中看到人们在阅读后感到困惑,所以这里是看待这种情况的另一种方式。

作为程序员,如果我想在内存中存储一​​个数组,我不能存储一个矩形的数字网格,因为计算机内存不是这样工作的,我必须以线性序列存储数字。

假设我有一个 2x2 矩阵,并在我的代码中像这样初始化它:

const matrix = [a, b, c, d];

我可以在代码的其他部分成功使用这个矩阵,前提是我知道每个数组元素代表什么。

OpenGL 规范定义了每个索引位置代表什么,这就是您构建数组并将其传递给 OpenGL 并使其执行您期望的操作所需的全部知识。

只有当我想在描述我的代码的文档中编写我的矩阵时,行或列的主要问题才会起作用,因为数学家将矩阵写为数字的矩形网格。然而,这只是一种约定,一种写东西的方式,对我编写的代码或计算机内存中数字的排列没有影响。您可以使用其他符号轻松重写这些数学论文,而且效果也一样。

对于上面的数组,我有两个选项可以在我的文档中将此数组作为矩形网格写入:

|a b|  OR  |a c|
|c d|      |b d|

无论我选择哪种方式编写文档,这都不会影响我的代码或计算机内存中数字的顺序,这只是文档。

为了让阅读我的文档的人知道我在程序中将值存储在线性数组中的顺序,我可以指定这是数组作为矩阵的列主要或行主要表示。如果它是列主要顺序,那么我应该遍历列以获得数字的线性排列。如果这是行主要表示,那么我应该遍历行以获得数字的线性排列。

一般来说,按行主要顺序编写文档会让程序员的生活更轻松,因为如果我想翻译这个矩阵

|a b c|
|d e f|
|g h i|

变成代码,我可以这样写:

const matrix = [
  a, b, c
  d, e, f
  g, h, i
];