1

我正在使用矩阵比较两个不同的 3D 图形线性数学库。以下是两个库中的两个类似的翻译函数:

static Matrix4<T> Translate(T x, T y, T z)
{
    Matrix4 m;
    m.x.x = 1; m.x.y = 0; m.x.z = 0; m.x.w = 0;
    m.y.x = 0; m.y.y = 1; m.y.z = 0; m.y.w = 0;
    m.z.x = 0; m.z.y = 0; m.z.z = 1; m.z.w = 0;
    m.w.x = x; m.w.y = y; m.w.z = z; m.w.w = 1;
    return m;
}

(来自 SO 用户的 c++ 库)

static inline void mat4x4_translate(mat4x4 T, float x, float y, float z)
{    
mat4x4_identity(T);
T[3][0] = x;
T[3][1] = y;
T[3][2] = z;
 }

(来自 SO 用户 datenwolf 的 linmath c 库)

我是新手,但我知道矩阵乘法的顺序很大程度上取决于您使用的是列优先格式还是行优先格式。

在我看来,这两个使用相同的格式,因为第一个索引被视为行,第二个索引是列。也就是说,两者x y z都应用于相同的第一个索引。这对我来说意味着行优先,因此矩阵乘法是左关联的(例如,您通常会rotate * translate按该顺序执行 a )。

我在左关联上下文中多次使用了第一个示例,并且它一直按预期工作。虽然我没有使用过第二种,但作者说它是右关联的,但我无法看到两者格式之间的差异。

4

2 回答 2

6

在我看来,这两个使用相同的格式,因为第一个索引被视为行,第二个索引是列。

外观可能具有欺骗性,但实际上 linmath.h 中的第一个索引是列。C 和 C++ 指定在这样定义的多维数组中

sometype a[n][m];

连续有nm个某种类型的元素。如果它是行或列主要顺序仅取决于您如何解释索引。现在 OpenGL 定义了 4×4 矩阵,以下面的线性方案进行索引

0 4 8 c
1 5 9 d
2 6 a e
3 7 b f

如果您应用 C++ 多维数组的规则,您将添加以下列行指定

   ----> n

|  0 4 8 c
|  1 5 9 d
V  2 6 a e
m  3 7 b f

它将线性索引重新映射为 2 元组

0 -> 0,0
1 -> 0,1
2 -> 0,2
3 -> 0,3
4 -> 1,0
5 -> 1,1
6 -> 1,2
7 -> 1,3
8 -> 2,0
9 -> 2,1
a -> 2,2
b -> 2,3
c -> 3,0
d -> 3,1
e -> 3,2
f -> 3,3

好的,OpenGL 和一些数学库使用列主排序,很好。但是为什么要这样做并打破通常的数学惯例,即在M i,j中索引i指定行而j指定列?因为它让事情看起来更好。你看,矩阵只是一堆向量。可以并且通常确实形成坐标基系统的向量。

看看这张照片:

具有右手基向量的 3 维笛卡尔坐标系

X、Y 和 Z 轴本质上是向量。它们被定义为

X = (1,0,0)
Y = (0,1,0)
Z = (0,0,1)

片刻,上面看起来不像一个单位矩阵吗?确实如此,而且确实如此

然而,按原样写成矩阵是通过堆叠行向量形成的。矩阵乘法的规则本质上告诉我们,由行向量形成的矩阵,通过左关联乘法将行向量转换为行向量。列主矩阵通过右关联乘法将列向量转换为列向量。

现在这不是一个真正的问题,因为左关联可以做与右关联可以做的相同的事情,您只需将行交换为列(即转置)所有内容并反转操作数的顺序。然而,左<>右行<>列只是我们写东西的符号约定。

典型的数学符号是(例如)

v_clip = P · V · M · v_local

这种符号可以直观地看到正在发生的事情。此外,在编程中,关键字符=通常指定从右到左的分配。一些编程语言在数学上的影响更大,比如 Pascal 或 Delphi 并编写它:=。无论如何,对于行主要排序,我们必须编写它

v_clip = v_local · M · V · P

对于大多数数学家来说,这看起来很不自然。因为,从技术上讲,M、V 和 P 实际上是线性运算符(是的,它们也是矩阵和线性变换),运算符总是介于等式/赋值和变量之间。

所以这就是我们使用列主要格式的原因:它看起来更好。从技术上讲,它也可以使用行主要格式来完成。这与矩阵的内存布局有什么关系?好吧,当您想使用列主要顺序表示法时,您希望直接访问变换矩阵的基向量,而不需要它们逐个元素地提取它们。通过以列主要格式存储数字,访问矩阵的某个基向量所需要的只是线性内存中的简单偏移量。

我不能说另一个库的代码示例,但我强烈假设它也将第一个索引视为较慢的递增索引,如果受 OpenGL 的表示法影响,这使得它可以在主要列中工作。请记住:列主要和右关联性 == 行主要和左关联性。

于 2013-03-23T11:25:51.233 回答
2

张贴的片段不足以回答这个问题。它们可以是按行顺序存储的行优先矩阵,也可以是按列顺序存储的列优先矩阵。

如果您查看与适当矩阵相乘时如何处理向量,则可能会更明显。在行优先系统中,您会期望向量被视为单行矩阵,而在列优先系统中,它同样会是单列矩阵。然后,这决定了如何将向量和矩阵相乘。您只能将向量与矩阵相乘作为右侧的单列或左侧的单行。

GL 约定是列优先的,因此向量向右相乘。D3D 是行优先的,所以向量是行并且向左相乘。

在连接转换时需要考虑到这一点,以便以正确的顺序应用它们。

IE:

GL:
    V' = CAMERA * WORLD * LOCAL * V
D3D:
    V' = V * LOCAL * WORLD * CAMERA

然而,他们选择存储他们的矩阵,使得内存中的表示实际上是相同的(直到我们进入着色器并且需要转置一些表示......)

于 2013-03-23T11:16:41.397 回答