矩阵4x4中最后一行的魔法4用于3D图形的目的是什么?

6 opengl camera matrix linear-algebra webgl

当我读到有关WebGL的书时,我已经看到了下一个矩阵描述:

在此输入图像描述

有关于书中最后一行的信息(WebGL初学者指南初学者指南迭戈康托尔,布兰登琼斯):

神秘的第四排第四排没有任何特殊意义.元素m4,m8,m12始终为零.元素m 16(齐次坐标)将始终为1.

所以,如果最后一行总是[ 0, 0, 0, 1 ],我不明白下一行:

为什么有必要严格[ 0, 0, 0, 1 ],为什么不是所有的值都是0或甚至其他值?

但是,如果要查看glMatrixjavascript库的源代码,请参考https://github.com/toji/gl-matrix/blob/master/src/gl-matrix/mat4.js中的方法translate().mat4

你能看到下一个:

/**
 * Translate a mat4 by the given vector not using SIMD
 *
 * @param {mat4} out the receiving matrix
 * @param {mat4} a the matrix to translate
 * @param {vec3} v vector to translate by
 * @returns {mat4} out
 */
mat4.scalar.translate = function (out, a, v) {
    var x = v[0], y = v[1], z = v[2],
        a00, a01, a02, a03,
        a10, a11, a12, a13,
        a20, a21, a22, a23;

    if (a === out) {
        out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
        out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
        out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
        out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
    } else {
        a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3];
        a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7];
        a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11];

        out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03;
        out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13;
        out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23;

        out[12] = a00 * x + a10 * y + a20 * z + a[12];
        out[13] = a01 * x + a11 * y + a21 * z + a[13];
        out[14] = a02 * x + a12 * y + a22 * z + a[14];
        out[15] = a03 * x + a13 * y + a23 * z + a[15];
    }

    return out;
};
Run Code Online (Sandbox Code Playgroud)

我将强调这一行:

out[15] = a03 * x + a13 * y + a23 * z + a[15];
Run Code Online (Sandbox Code Playgroud)

最后一个(齐次坐标)正在修改,所以它可能不等于1.0?

所以,我宁愿不明白......

我看到,内部3x3矩阵表示旋转并且[ m13, m14, m15 ]是用于改变相机原点位置的平移向量,但是最后一行是什么以及为什么有时我在库中看到它的一些计算?

PS

另外我想有一些magic 3用于2D变换的3x3矩阵,对吗?

BDL*_*BDL 11

让我们从一些理论开始:

通常,OpenGL中的所有变换都是不同向量空间之间的映射.这意味着转换t从空间V获取一个元素并将其映射到它在空间W中的相应元素,这可以写为

t: V ---> W
Run Code Online (Sandbox Code Playgroud)

最简单的映射之一是线性映射,它可以(在某些假设下**)始终用矩阵表示.矩阵的维数总是由我们正在处理的向量空间的维度给出,因此从R ^ N到R ^ M的映射将始终如下所示:

t: R^N ---> R^M
t(x) = A * x, A = R^(N,M)
Run Code Online (Sandbox Code Playgroud)

其中A是N倍M维矩阵.

在OpenGL中,我们通常需要从R ^ 3到R ^ 3的映射,这意味着线性映射将始终由3x3矩阵表示.使用它,可以至少表达旋转,缩放(以及这种***的组合).但是在查看(例如)翻译时,我们发现无法使用3x3矩阵表示它们,因此我们必须扩展我们的转换以支持此操作.

这可以通过使用仿射映射而不是线性映射来实现,其被定义为

t: R^N ---> R^M
t(x) = A * x + b,  A = R^(N,M) is a linear transformation and  b = R^M
Run Code Online (Sandbox Code Playgroud)

使用这个,我们可以通过指定3x3矩阵加上3D矢量来表示从R ^ 3到R ^ 3的旋转,缩放和变换.由于这个公式不是很方便(需要矩阵和矢量,难以组合多个变换),因此通常将操作存储在维度为N + 1的矩阵中,称为增广矩阵(或增广向量空间):

t: R^N ---> R^M

         -A-  b       x
t(x) = [        ] * [   ]
         -0-  1       1
Run Code Online (Sandbox Code Playgroud)

如您所见,矩阵的最后一行始终为零,除了最右边的元素是1.这也保证了结果t(x)的最后一个维度始终为1.

为什么有必要严格[ 0, 0, 0, 1 ],为什么不是所有的值都是0或甚至其他值?

如果我们不将最后一行限制为精确[0,0,0,1],我们将不再在R ^ 3中具有增强的仿射映射,而是在R ^ 4中的线性映射.由于在OpenGL中R ^ 4并不真正相关,我们希望保留包含的翻译,因此最后一行是固定的.另一点是,当最后一行不同时,通过矩阵乘法组合仿射映射将不起作用.

剩下的一个问题是,我们仍然无法通过使用仿射映射来表达(透视)投影.当在OpenGL中查看透视投影矩阵时,会注意到最后一行不是[0,0,0,1],但这背后的理论是完全不同的故事(如果您有兴趣看看这里这里).

关于最后一行是什么以及为什么有时我会在库中看到它的一些计算?最后一个(齐次坐标)正在修改,所以它可能不等于1.0?

如前所述,最后一行仅[0,0,0,1]用于仿射映射,而不是用于投射映射.但有时在投影后应用变换是有意义的(例如在屏幕上移动投影图像),然后必须遵守矩阵的最后一行.这就是为什么大多数矩阵库以允许一般矩阵的方式实现所有操作的原因.这条线

out[15] = a03 * x + a13 * y + a23 * z + a[15];
Run Code Online (Sandbox Code Playgroud)

只要最后一行(a03,a13,a23,a [15])等于1,就会得到1 [0,0,0,1].

由于这个帖子已经比我想象的要长很多,我最好停在这里,但是如果你有任何进一步的问题,请问我会尝试在答案中加入一些东西.

脚注:

**当两个空间都是有限维向量空间并且为它们两者定义基础时起作用.

***组合,因为有限维空间上的线性变换的组合也是线性的,例如,t:R ^ N - > R ^ M,u:R ^ M - > R ^ K,均线性=> t (u(x))线性