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

Mar*_*ram 71 c++ opengl math 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 } };
Run Code Online (Sandbox Code Playgroud)

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];
Run Code Online (Sandbox Code Playgroud)

因此,matrixB[0]= row 0,matrixB[1]= row 1等.再次,这表示行主要布局.

当我创建一个如下所示的翻译矩阵时,我的问题/困惑就出现了:

1, 0, 0, transX
0, 1, 0, transY
0, 0, 1, transZ
0, 0, 0, 1
Run Code Online (Sandbox Code Playgroud)

这在记忆中被列为,{ 1, 0, 0, transX, 0, 1, 0, transY, 0, 0, 1, transZ, 0, 0, 0, 1 }.

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

如果转置为GL_FALSE,则假定每个矩阵按列主要顺序提供.如果转置为GL_TRUE,则假定每个矩阵按行主要顺序提供.

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

Sig*_*erm 66

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
Run Code Online (Sandbox Code Playgroud)

要么

{ 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 }
Run Code Online (Sandbox Code Playgroud)
  • x,y,z是描述矩阵坐标系的3分量矢量(相对于全局坐标系的局部坐标系).

  • p是描述矩阵坐标系原点的3分量矢量.

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

{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, transX, transY, transZ, 1 }.
Run Code Online (Sandbox Code Playgroud)

把它留在那,其余的应该很容易.

---引用旧的opengl faq--


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

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

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

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


  • 我仍然对这个答案的存在感到困惑。在行之间阅读,您可以删除整个答案,将其替换为“默认情况下为列专业,如果您在上传矩阵时设置转置标志,则为行专业”。它会说出我们想知道的一切,而不会避免回答实际问题的不必要的过度简化。这也意味着默认情况下您将向量左乘于变换矩阵(“mat * vec”)。把这个留在这里是因为我知道我会忘记这个,再次寻找它并找到这个无意义的答案,并且比我再次更加困惑...... (7认同)
  • 列和行主要是关于矩阵如何在内存中布局.看到.例如.书籍Golub - Matrix Computations,或者关于行专业的Wiki文章.OpenGL FAQ是错误的,或含糊不清的.将向量视为行向量还是向量向量是一个单独的问题,这也决定了是否应将基础向量保留在列或行中.(有时使用术语row-major来指代这一点,增加了混淆.)GLSL的矩阵数据类型是列主要的,并且惯例是将向量视为列向量.在内存中,D3D和OpenGL约定最终是相同的. (6认同)
  • 你能否解释一下你认为列/行主要实际意味着什么?我根本不理解"忘记......"这句话.根据[Wikipedia](https://en.wikipedia.org/wiki/Row-major_order),这些术语定义了内存布局,这是此处最重要的部分.我为什么要忘记这一点? (4认同)
  • 你是什​​么意思相同的内存布局?鉴于我原始帖子中的示例,它表明我的矩阵的内存布局是行主要的(因为这是C在二维数组中布局内存的方式).那么为什么我需要设置标志来告诉OpenGL我使用的是column-major,当内存布局不匹配时? (2认同)
  • @MarkIngram:行/列是仅在文档中使用的符号.在D3DMatrix和OpenGL矩阵中,转换元素占据存储器中的相同位置 - 具有索引/偏移12,13,14(或16个元素阵列的第13,14和15个元素)的元素. (2认同)
  • @MarkIngram:换句话说,OpenGL文档中使用的矩阵表示法并没有描述OpenGL矩阵的内存中布局. (2认同)
  • DX和OpenGL具有相同的内存布局,但不是相同的约定.这意味着DirectX在数学意义上存储转置矩阵.这也意味着你必须通勤所有的操作(wrt数学).例如在DX中,Tr*Rot = TrRot,在opengl中Rot*Tr = TrRot. (2认同)

Rob*_*rto 53

总结SigTerm和dsharlet的答案:在GLSL中转换向量的常用方法是将向量乘以变换矩阵:

mat4 T; vec4 v; vec4 v_transformed; 
v_transformed = T*v;
Run Code Online (Sandbox Code Playgroud)

为了使其工作,OpenGL期望内存布局T,如SigTerm所述,

{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, transX, transY, transZ, 1 }
Run Code Online (Sandbox Code Playgroud)

这也被称为"专业专栏".但是,在着色器代码中(如注释所示),您可以通过变换矩阵右对乘矢量:

v_transformed = v*T;
Run Code Online (Sandbox Code Playgroud)

只有T转置时才会产生正确的结果,即具有布局

{ 1, 0, 0, transX, 0, 1, 0, transY, 0, 0, 1, transZ, 0, 0, 0, 1 }
Run Code Online (Sandbox Code Playgroud)

(即'行专业').由于您已经为着色器提供了正确的布局,即row major,因此没有必要设置transpose标记glUniform4v.

  • 在我看来最好的答案.这是图形学生的完美范例.在数学中,一个向量是水平的而另一个是垂直的(否则产品是未定义的).我知道编程时我们忘记了大多数数学形式XD (5认同)
  • @LiuHao但这是真的,如果你打算将转换后的向量计算为 Tv 。那么 (1,0,0,0), (0,1,0,0), (0,0,1,0), (transX, transY, transZ,1) 就是 T 的列,这使得布局专栏专业。如果您打算对内存中的相同数据数组使用 vT 约定,则必须将其标记为行主要,因为在这种情况下,T 的行必须是 (1,0,0,0), (0,1, 0,0), (0,0,1,0), (transX, transY, transZ,1) 才能工作。 (2认同)

dsh*_*let 14

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

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

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

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

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

  • @MarkIngram 它告诉您您正在将您的位置解释为行向量。当你想用矩阵 A 变换向量 x 时,你计算 x'^T = x^T A。这不是一般数学中的标准,但在计算机图形学中很常见(不幸的是,它会导致很多混乱)。 (2认同)
  • 所以我应该改变乘法的顺序?而不是'矢量x模型x视图x投影;`,做`投影x视图x模型x矢量;`? (2认同)