简体   繁体   English

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

[英]Confusion between C++ and OpenGL matrix order (row-major vs column-major)

I'm getting thoroughly confused over matrix definitions.我对矩阵定义感到非常困惑。 I have a matrix class, which holds a float[16] which I assumed is row-major, based on the following observations:我有一个矩阵 class,它包含一个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 and matrixB both have the same linear layout in memory (ie all numbers are in order). matrixAmatrixB在 memory 中都具有相同的线性布局(即所有数字均按顺序排列)。 According to http://en.wikipedia.org/wiki/Row-major_order this indicates a row-major layout.根据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];

Therefore, matrixB[0] = row 0, matrixB[1] = row 1, etc. Again, this indicates row-major layout.因此, matrixB[0] = row 0, matrixB[1] = row 1,等等。同样,这表示行优先布局。

My problem / confusion comes when I create a translation matrix which looks like:当我创建一个看起来像这样的翻译矩阵时,我的问题/困惑就来了:

1, 0, 0, transX
0, 1, 0, transY
0, 0, 1, transZ
0, 0, 0, 1

Which is laid out in memory as, { 1, 0, 0, transX, 0, 1, 0, transY, 0, 0, 1, transZ, 0, 0, 0, 1 } .在 memory 中布局为{ 1, 0, 0, transX, 0, 1, 0, transY, 0, 0, 1, transZ, 0, 0, 0, 1 }

Then when I call glUniformMatrix4fv , I need to set the transpose flag to GL_FALSE, indicating that it's column-major, else transforms such as translate / scale etc don't get applied correctly:然后,当我调用glUniformMatrix4fv时,我需要将转置标志设置为 GL_FALSE,表明它是列优先的,否则无法正确应用转换/缩放等转换:

If transpose is GL_FALSE, each matrix is assumed to be supplied in column major order.如果 transpose 为 GL_FALSE,则假定每个矩阵都按列主要顺序提供。 If transpose is GL_TRUE, each matrix is assumed to be supplied in row major order.如果 transpose 为 GL_TRUE,则假定每个矩阵都按行主要顺序提供。

Why does my matrix, which appears to be row-major, need to be passed to OpenGL as column-major?为什么我的矩阵看起来是行优先的,需要作为列优先传递给 OpenGL?

matrix notation used in opengl documentation does not describe in-memory layout for OpenGL matrices opengl 文档中使用的矩阵表示法没有描述 OpenGL 矩阵的内存布局

If think it'll be easier if you drop/forget about the entire "row/column-major" thing.如果您认为放弃/忘记整个“行/列主要”的事情会更容易。 That's because in addition to row/column major, the programmer can also decide how he would want to lay out the matrix in the memory (whether adjacent elements form rows or columns), in addition to the notation, which adds to confusion.这是因为除了行/列专业之外,程序员还可以决定他希望如何在内存中布置矩阵(相邻元素形成行还是列),除了符号,这会增加混乱。

OpenGL matrices have same memory layout as directx matrices . 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

or或者

{ 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 are 3-component vectors describing the matrix coordinate system (local coordinate system within relative to the global coordinate system). x、y、z 是描述矩阵坐标系(相对于全局坐标系内的局部坐标系)的 3 分量向量。

  • p is a 3-component vector describing the origin of matrix coordinate system. p 是描述矩阵坐标系原点的三分量向量。

Which means that the translation matrix should be laid out in memory like this:这意味着翻译矩阵应该像这样在内存中布局:

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

Leave it at that, and the rest should be easy.把它留在那里,其余的应该很容易。

---citation from old opengl faq-- ---来自旧的opengl常见问题解答--


9.005 Are OpenGL matrices column-major or row-major? 9.005 OpenGL 矩阵是列优先还是行优先?

For programming purposes, OpenGL matrices are 16-value arrays with base vectors laid out contiguously in memory.出于编程目的,OpenGL 矩阵是 16 值数组,其基向量在内存中连续布局。 The translation components occupy the 13th, 14th, and 15th elements of the 16-element matrix, where indices are numbered from 1 to 16 as described in section 2.11.2 of the OpenGL 2.1 Specification.平移分量占据 16 元素矩阵的第 13、14 和 15 个元素,其中索引从 1 到 16 编号,如 OpenGL 2.1 规范的第 2.11.2 节所述。

Column-major versus row-major is purely a notational convention.列优先与行优先纯粹是一种符号约定。 Note that post-multiplying with column-major matrices produces the same result as pre-multiplying with row-major matrices.请注意,与列优先矩阵的后乘法产生与行优先矩阵的预乘相同的结果。 The OpenGL Specification and the OpenGL Reference Manual both use column-major notation. OpenGL 规范和 OpenGL 参考手册都使用列优先表示法。 You can use any notation, as long as it's clearly stated.您可以使用任何符号,只要清楚说明即可。

Sadly, the use of column-major format in the spec and blue book has resulted in endless confusion in the OpenGL programming community.可悲的是,在规范和蓝皮书中使用列优先格式导致 OpenGL 编程社区无休止的混乱。 Column-major notation suggests that matrices are not laid out in memory as a programmer would expect.列优先表示法表明矩阵没有像程序员所期望的那样布置在内存中。


I'm going to update this 9 years old answer.我将更新这个 9 岁的答案。

A mathematical matrix is defined as mxn matrix.一个数学矩阵被定义为mxn矩阵。 Where m is a number of rows and n is number of columns .其中m数, n列数 For the sake of completeness, rows are horizontals, columns are vertical.为了完整起见,行是水平的,列是垂直的。 When denoting a matrix element in mathematical notation Mij , the first element ( i ) is a row index, the second one ( j ) is a column index.当用数学符号Mij表示矩阵元素时,第一个元素 ( i ) 是行索引,第二个元素 ( j ) 是列索引。 When two matrices are multiplied, ie A(mxn) * B(m1 x n1) , the resulting matrix has number of rows from the first argument( A ), and number of columns of the second( B ), and number of columns of the first argument ( A ) must match number of rows of the second ( B ).当两个矩阵相乘时,即A(mxn) * B(m1 x n1) ,得到的矩阵具有第一个参数的行数( A ),第二个参数的列数( B ),以及第一个参数 ( A ) 必须匹配第二个 ( B ) 的行数。 so n == m1 .所以n == m1 Clear so far, yes?到目前为止清楚,是吗?

Now, regarding in-memory layout.现在,关于内存布局。 You can store matrix two ways.您可以通过两种方式存储矩阵。 Row-major and column-major.行优先和列优先。 Row-major means that effectively you have rows laid out one after another, linearly. Row-major 意味着有效地你有一个又一个线性排列的行。 So, elements go from left to right, row after row.所以,元素从左到右,一行接一行。 Kinda like english text.有点像英文文本。 Column-major means that effectively you have columns laid out one after another, linearly. Column-major 意味着有效地你有一个又一个线性排列的 So elements start at top left, and go from top to bottom.所以元素从左上角开始,从上到下。

Example:例子:

//matrix
|a11 a12 a13|
|a21 a22 a23|
|a31 a32 a33|

//row-major
[a11 a12 a13 a21 a22 a23 a31 a32 a33]

 //column-major
[a11 a21 a31 a12 a22 a32 a13 a23 a33]

Now, here's the fun part!现在,这是有趣的部分!

There are two ways to store 3d transformation in a matrix.有两种方法可以将 3d 变换存储在矩阵中。 As I mentioned before, a matrix in 3d essentially stores coordinate system basis vectors and position.正如我之前提到的,3d 中的矩阵本质上存储坐标系基向量和位置。 So, you can store those vectors in rows or in columns of a matrix.因此,您可以将这些向量存储在矩阵的行或列中。 When they're stored as columns, you multiply a matrix with a column vector.当它们存储为列时,您将矩阵与列向量相乘。 Like this.像这样。

//convention #1
|vx.x vy.x vz.x pos.x|   |p.x|   |res.x|
|vx.y vy.y vz.y pos.y|   |p.y|   |res.y|
|vx.z vy.z vz.z pos.z| x |p.z| = |res.z|
|   0    0    0     1|   |  1|   |res.w| 

However, you can also store those vectors as rows, and then you'll be multiplying a row vector with a matrix:但是,您也可以将这些向量存储为行,然后将行向量与矩阵相乘:

//convention #2 (uncommon)
                  | vx.x  vx.y  vx.z 0|   
                  | vy.x  vy.y  vy.z 0|   
|p.x p.y p.z 1| x | vz.x  vz.y  vz.z 0| = |res.x res.y res.z res.w|
                  |pos.x pos.y pos.z 1|   

So.所以。 Convention #1 often appears in mathematical texts.约定#1 经常出现在数学课本中。 Convention #2 appeared in DirectX sdk at some point.约定 #2 在某个时候出现在 DirectX sdk 中。 Both are valid.两者都是有效的。

And in regards of the question, if you're using convention #1, then your matrices are column-major.关于这个问题,如果您使用约定#1,那么您的矩阵是列优先的。 And if you're using convention #2, then they're row major.如果您使用约定 #2,那么它们是行专业的。 However, memory layout is the same in both cases但是,两种情况下的内存布局是相同的

[vx.x vx.y vx.z 0 vy.x vy.y vy.z 0 vz.x vz.y vz.z 0 pos.x pos.y pos.z 1]

Which is why I said it is easier to memorize which element is which, 9 years ago.这就是为什么我说在 9 年前更容易记住哪个元素是哪个元素。

To summarize the answers by SigTerm and dsharlet: The usual way to transform a vector in GLSL is to right-multiply the transformation matrix by the vector:总结 SigTerm 和 dsharlet 的答案: 在 GLSL 中变换向量的常用方法是将变换矩阵与向量右乘:

mat4 T; vec4 v; vec4 v_transformed; 
v_transformed = T*v;

In order for that to work, OpenGL expects the memory layout of T to be, as described by SigTerm,为了使其工作,OpenGL 期望T的内存布局为,如 SigTerm 所述,

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

which is also called 'column major'.这也被称为“列专业”。 In your shader code (as indicated by your comments), however, you left-multiplied the transformation matrix by the vector:但是,在您的着色器代码中(如您的评论所示),您将变换矩阵左乘以向量:

v_transformed = v*T;

which only yields the correct result if T is transposed, ie has the layout只有在T被转置时才会产生正确的结果,即具有布局

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

(ie 'row major'). (即“行专业”)。 Since you already provided the correct layout to your shader, namely row major, it was not necessary to set the transpose flag of glUniform4v .由于您已经为着色器提供了正确的布局,即行专业,因此没有必要设置glUniform4vtranspose标志。

You are dealing with two separate issues.您正在处理两个不同的问题。

First, your examples are dealing with the memory layout.首先,您的示例正在处理内存布局。 Your [4][4] array is row major because you've used the convention established by C multi-dimensional arrays to match your linear array.您的 [4][4] 数组是行主要的,因为您使用了 C 多维数组建立的约定来匹配您的线性数组。

The second issue is a matter of convention for how you interpret matrices in your program.第二个问题是关于如何在程序中解释矩阵的约定问题。 glUniformMatrix4fv is used to set a shader parameter. glUniformMatrix4fv用于设置着色器参数。 Whether your transform is computed for a row vector or column vector transform is a matter of how you use the matrix in your shader code.是否为行向量列向量转换计算转换取决于您如何在着色器代码中使用矩阵。 Because you say you need to use column vectors, I assume your shader code is using the matrix A and a column vector x to compute x' = A x .因为您说您需要使用列向量,所以我假设您的着色器代码使用矩阵A和列向量x来计算x' = A x

I would argue that the documentation of glUniformMatrix is confusing.我认为glUniformMatrix的文档令人困惑。 The description of the transpose parameter is a really roundabout way of just saying that the matrix is transposed or it isn't.转置参数的描述是一种非常迂回的方式,只是说矩阵是转置的还是不是。 OpenGL itself is just transporting that data to your shader, whether you want to transpose it or not is a matter of convention you should establish for your program. OpenGL 本身只是将该数据传输到您的着色器,无论您是否想要转置它是您应该为您的程序建立的约定问题。

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

I think that the existing answers here are very unhelpful, and I can see from the comments that people are left feeling confused after reading them, so here is another way of looking at this situation.我认为这里现有的答案非常无用,我可以从评论中看到人们在阅读后感到困惑,所以这是看待这种情况的另一种方式。

As a programmer, if I want to store an array in memory, I cannot store a rectangular grid of numbers, because computer memory doesn't work like that, I have to store the numbers in a linear sequence.作为程序员,如果我想在内存中存储一​​个数组,我不能存储一个矩形网格的数字,因为计算机内存不能这样工作,我必须以线性序列存储数字。

Lets say I have a 2x2 matrix and I initialize it in my code like this:假设我有一个 2x2 矩阵,我在我的代码中初始化它,如下所示:

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

I can successfully use this matrix in other parts of my code provided I know what each of the array elements represents.只要我知道每个数组元素代表什么,我就可以在代码的其他部分成功地使用这个矩阵。

The OpenGL specification defines what each index position represents, and this is all you need to know to construct an array and pass it to OpenGL and have it do what you expect. OpenGL 规范定义了每个索引位置所代表的内容,这就是构建数组并将其传递给 OpenGL 并让它执行您期望的操作所需要知道的全部内容。

The row or column major issue only comes into play when I want to write my matrix in a document that describes my code, because mathematicians write matrixes as rectangular grids of numbers.只有当我想在描述我的代码的文档中编写矩阵时,行或列的主要问题才会发挥作用,因为数学家将矩阵写成矩形的数字网格。 However this is just a convention, a way of writing things down, and has no impact on the code I write or the arrangement of numbers in memory on my computer.然而,这只是一种约定,一种写下来的方式,对我编写的代码或计算机内存中数字的排列没有影响。 You could easily re-write these mathematics papers using some other notation, and it would work just as well.您可以使用其他符号轻松地重写这些数学论文,并且效果也一样。

For the array above, I have two options for writing this array in my documentation as a rectangular grid:对于上面的数组,我有两个选项可以在我的文档中将此数组写为矩形网格:

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

Whichever way I choose to write my documentation, this will have no impact on my code or the order of the numbers in memory on my computer, it's just documentation.无论我选择哪种方式编写文档,这都不会影响我的代码或计算机内存中数字的顺序,它只是文档。

In order for people reading my documentation to know the order that I stored the values in the linear array in my program, I can specify that this is a column major or row major representation of the array as a matrix.为了让阅读我的文档的人知道我在程序中将值存储在线性数组中的顺序,我可以指定这是矩阵的列主要或行主要表示形式。 If it is in column major order then I should traverse the columns to get the linear arrangement of numbers.如果它是列主要顺序,那么我应该遍历列以获得数字的线性排列。 If this is a row major representation then I should traverse the rows to get the linear arrangement of numbers.如果这是行主要表示,那么我应该遍历行以获得数字的线性排列。

In general, writing documentation in row major order makes life easier for programmers, because if I want to translate this matrix一般来说,按行主要顺序编写文档会使程序员的生活更轻松,因为如果我想翻译这个矩阵

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

into code, I can write it like this:成代码,我可以这样写:

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

For example:例如:

GLM stores matrix values as m[4][4]. GLM 将矩阵值存储为 m[4][4]。 But it treats matrices as if they have a column major order.但它将矩阵视为具有列主要顺序。 Even though for 2 dimensional array m[ x ][ y ] in C x represents a row and y represents a column, which means that matrix represented by this array has in fact row major order.即使对于 C 中的二维数组 m[ x ][ y ], x表示一行, y表示一列,这意味着该数组表示的矩阵实际上具有行主序。 The trick is to treat m[ x ][ y ] as if x represents a column and y represents a row.诀窍是将 m[ x ][ y ] 视为x代表一列, y代表一行。 It is like you transposing the matrix without performing any additional operations to achieve that.这就像你转置矩阵而不执行任何额外的操作来实现它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM