GLKit的GLKMatrix是如何"列主序(Column Major)"的?

16

前提A

当谈到线性内存中的“列主”矩阵时,列是依次指定的,因此内存中的前4个条目对应于矩阵中的第一列。另一方面,“行主”矩阵被理解为按顺序指定行,因此内存中的前4个条目指定矩阵的第一行。


GLKMatrix4看起来像这样:

union _GLKMatrix4
{
    struct
    {
        float m00, m01, m02, m03;
        float m10, m11, m12, m13;
        float m20, m21, m22, m23;
        float m30, m31, m32, m33;
    };
    float m[16];
}
typedef union _GLKMatrix4 GLKMatrix4;

The documentation for the m member states that it is "A one-dimensional array of the matrix's elements in column-major order." However, according to Premise B, a "row" in a GLKMatrix4 is declared horizontally as a set of 4 floats. For example, [m00, m01, m02, m03] would be the first "row" and interpreted as mRowCol with m12 representing the entry at row 1, column 2. Looking at how the GLKMatrix struct members are laid out, the first four entries represent the first row of the matrix rather than the first column. Therefore, the conclusion is that m is not actually Column Major, contradicting the documentation. However, the author notes that they do not necessarily believe this conclusion, and questions whether it makes sense to define a "row" as vertical and a "column" as horizontal.


float m00, m01, m02, m03;是第一个“列”的声明,尽管它“看起来像”一行。这就是__前提B__失败的地方。 - bobobobo
3个回答

11

这个声明有点令人困惑,但矩阵是以列优先顺序排列的。结构体中的四行代表矩阵中的列,其中m0*代表第0列,m3*代表第3列。这很容易验证,只需创建一个平移矩阵并检查平移组件的m30、m31和m32值即可。

我猜你的困惑可能来自于结构体将浮点数按行排列的事实,而它们实际上表示的是列。


1
那个,还有我一直通过想象m(row)(col)而不是m(col)(row)来索引矩阵的事实。谢谢。 - Matt Wilding
@MattWilding 这正是让我困惑的地方。但是m(row)(col)是访问_行优先_矩阵的方式,即先行后列。令事情变得混乱的是,尽管MATLAB是“列优先”的,但它仍然具有m(row,col)的行优先索引约定。 - bobobobo
我们往往认为名为“mXY”的变量/成员是矩阵中第X行和第Y列的元素,但由于该矩阵被声明为按列主序存储,因此每个成员名称的第一个“子索引”(在这种情况下为X)是列。 - Nicolas Miari

10
这段内容来自OpenGL规范--

column major

混淆的关键在于:正如其他人所指出的那样,我们使用第一个索引表示列而非行来索引列主矩阵:

  • m00 指代 列=0,行=0
  • m01 指代 列=0,行=1
  • m02 指代 列=0,行=2

MATLAB 在内部数据表示方面确实使用列主,但是它仍然使用 行主 的索引约定 x(row,col)。我不确定为什么他们这样做。

另外需要注意的是,默认情况下 OpenGL 使用 列向量 -- 也就是说,你需要在着色器中将矩阵与它所转换的向量进行后乘操作,即 (MATRIX*VECTOR)。这与行主矩阵所需的 (VECTOR*MATRIX) 相反。

阅读我关于C语言中行主序和列主序矩阵的文章可能会有所帮助

在编写代码时,按列主序排列矩阵是不直观的

越看越觉得在C语言中按列主序工作是个错误,因为需要进行心理转换。当你在代码中排列一个矩阵时,由于我们语言的从左到右的特性,你只能按行写出矩阵:

float a[4] = { 1, 2,
               3, 4 };

因此,看起来您很自然地正在按行指定矩阵

1 2
3 4

但是,如果您使用的是列主序规范,则实际上已经指定了矩阵

1 3
2 4

这真的很反直觉。如果我们使用纵向(或“列主”语言),在代码中指定列主矩阵会更容易。

所有这些是为Direct3D进行的另一轮争论吗?我不知道,你告诉我。

实际上,为什么OpenGL使用列主矩阵呢?

深入挖掘,似乎之所以这样做是为了能够通过向量“后乘”矩阵,如(MATRIX*VECTOR)——即能够像使用列(主)向量 一样使用:

列主矩阵乘法

┌ 2 8 1 1 ┐ ┌ 2 ┐
│ 2 1 7 2 │ │ 2 │
│ 2 6 5 1 │ │ 2 │
└ 1 9 0 0 ┘ └ 1 ┘

Contrast this with having to use row vectors: 与使用行向量相比,可以对比一下:
行主元矩阵乘法:
[ 2 2 2 1 ]  ┌ 2 8 1 1 ┐ 
             │ 2 1 7 2 │
             │ 2 6 5 1 │
             └ 1 9 0 0 ┘

如果矩阵是按行主序指定的,则“应该”使用行向量,并通过它们要转换的向量对矩阵进行预乘。使用行向量的原因是“一致的数据表示”:毕竟,行向量只是一个1行4列的矩阵。

3
Premise B 中的问题在于您假设 GLKMatrix4 中的“行”是横向声明的四个浮点数集合([m00,m01,m02,m03] 将是第一行)。

我们可以通过检查以下代码中的 column 的值来验证:

GLKMatrix3 matrix = {1, 2, 3, 4, 5, 6, 7, 8, 9};
GLKVector3 column = GLKMatrix3GetColumn(m, 0);

注意:为了简单起见,我使用了GLKMatrix3,但同样适用于GLKMatrix4

是的,这证实了我的怀疑。谢谢。 - Matt Wilding

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接