3D 图形矩阵 4x4 中最后一行的 magic 4 的目的是什么?
What's the purpose of magic 4 of last row in matrix 4x4 for 3D graphics?
当我阅读有关 WebGL 的书时,我看到了下一个矩阵描述:
本书(WebGL 新手指南新手指南 Diego Cantor, Brandon Jones)的最后一行有信息:
The mysterious fourth row The fourth row does not bear any special
meaning. Elements m4, m8, m12 are always zero. Element m 16 (the
homogeneous coordinate) will always be 1.
所以,如果最后一行总是[ 0, 0, 0, 1 ]
,我不明白下一个:
Why is it necessary be strictly [ 0, 0, 0, 1 ]
, why not just all
the values be 0 or even some other value?
但是,如果要查看 glMatrix
javascript 库的源代码,正是 mat4
https://github.com/toji/gl-matrix/blob/master/src/gl-matrix/mat4.js[=23= 中的方法 translate()
]
您可以看到下一个:
/**
* 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;
};
我将突出显示以下行:
out[15] = a03 * x + a13 * y + a23 * z + a[15];
最后一个(齐次坐标)正在修改,所以它可能不等于1.0?
所以,我宁愿不明白...
我明白了,内部 3x3 矩阵表示旋转,[ m13, m14, m15 ]
是用于改变相机原点位置的平移向量,但最后一行是什么以及为什么有时我会在库中看到一些关于它的计算?
PS
另外我想对于用于 2D 变换的 3x3 矩阵有某种 magic 3
,对吗?
让我们从一些理论开始:
一般来说,OpenGL 中的所有变换都是不同向量 space 之间的映射。这意味着转换 t 从 space V 中获取一个元素并将其映射到 space W 中的相应元素,可以写成
t: V ---> W
最简单的映射之一是 linear map,它(在某些假设下**)总是可以用矩阵表示。矩阵的维数总是由我们正在处理的向量 spaces 的维数给出,因此从 R^N 到 R^M 的映射总是如下所示:
t: R^N ---> R^M
t(x) = A * x, A = R^(N,M)
其中A是N乘以M维的矩阵。
在 OpenGL 中,我们通常需要从 R^3 到 R^3 的映射,这意味着线性映射将始终由 3x3 矩阵表示。使用它,至少可以表达旋转、缩放(以及这***的组合)。但是在查看(例如)翻译时,我们发现无法使用 3x3 矩阵表示它们,因此我们必须扩展我们的转换以支持此操作。
这可以通过使用 affine mappings 而不是线性的来实现,线性的定义为
t: R^N ---> R^M
t(x) = A * x + b, A = R^(N,M) is a linear transformation and b = R^M
使用它,我们可以通过指定 3x3 矩阵和 3D 向量来表达从 R^3 到 R^3 的旋转、缩放和变换。由于这个公式不是很方便(需要一个矩阵和一个向量,难以组合多个变换),所以通常将操作存储在一个N+1维的矩阵中,称为增广矩阵(或增广向量space小号):
t: R^N ---> R^M
-A- b x
t(x) = [ ] * [ ]
-0- 1 1
如您所见,矩阵的最后一行始终为零,除了最右边的元素为 1。这也保证了结果 t(x) 的最后一个维度始终为 1。
Why is it necessary be strictly [ 0, 0, 0, 1 ]
, why not just all the values be 0 or even some other value?
如果我们不将最后一行限制为恰好 [0,0,0,1]
,我们将不再在 R^3 中有增强仿射映射,而是在 R^4 中有线性映射。由于在 OpenGL 中 R^4 并不是真正相关的,而且我们希望保留翻译,所以最后一行是固定的。还有一点,当最后一行不同时,通过矩阵乘法组合仿射映射是行不通的。
剩下的一个问题是,我们仍然无法使用仿射映射来表达(透视)投影。在OpenGL中查看透视投影矩阵时,会注意到这里的最后一行不是[0,0,0,1]
,但背后的理论是完全不同的故事(如果有兴趣可以看看here or here) .
What's about the last row and why sometimes I see some calculations on it in libraries? The last one ( the homogeneous coordinate ) is modifying, so it could be not equal 1.0?
如前所述,最后一行仅 [0,0,0,1]
用于仿射映射,不适用于投影映射。但有时在投影后应用变换(例如在屏幕上移动投影图像)是有意义的,然后矩阵的最后一行必须得到尊重。这就是为什么大多数矩阵库以允许通用矩阵的方式实现所有操作的原因。行
out[15] = a03 * x + a13 * y + a23 * z + a[15];
只要最后一行 (a03, a13, a23, a[15]) 等于 [0,0,0,1]
.
就会得到 1
因为这篇 post 已经比我想象的要长很多,我最好就此打住,但如果您有任何其他问题,请尽管提问,我会尝试在答案中添加一些内容。
脚注:
** 当两个 space 都是有限维向量 space 并且为它们定义了一个基础时有效。
*** 组合,因为有限维space上的线性变换的组合也是线性的,例如,t: R^N -> R^M, u: R^M -> R^K, 都是线性的 => t(u(x)) 线性的
当我阅读有关 WebGL 的书时,我看到了下一个矩阵描述:
本书(WebGL 新手指南新手指南 Diego Cantor, Brandon Jones)的最后一行有信息:
The mysterious fourth row The fourth row does not bear any special meaning. Elements m4, m8, m12 are always zero. Element m 16 (the homogeneous coordinate) will always be 1.
所以,如果最后一行总是[ 0, 0, 0, 1 ]
,我不明白下一个:
Why is it necessary be strictly
[ 0, 0, 0, 1 ]
, why not just all the values be 0 or even some other value?
但是,如果要查看 glMatrix
javascript 库的源代码,正是 mat4
https://github.com/toji/gl-matrix/blob/master/src/gl-matrix/mat4.js[=23= 中的方法 translate()
]
您可以看到下一个:
/**
* 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;
};
我将突出显示以下行:
out[15] = a03 * x + a13 * y + a23 * z + a[15];
最后一个(齐次坐标)正在修改,所以它可能不等于1.0?
所以,我宁愿不明白...
我明白了,内部 3x3 矩阵表示旋转,[ m13, m14, m15 ]
是用于改变相机原点位置的平移向量,但最后一行是什么以及为什么有时我会在库中看到一些关于它的计算?
PS
另外我想对于用于 2D 变换的 3x3 矩阵有某种 magic 3
,对吗?
让我们从一些理论开始:
一般来说,OpenGL 中的所有变换都是不同向量 space 之间的映射。这意味着转换 t 从 space V 中获取一个元素并将其映射到 space W 中的相应元素,可以写成
t: V ---> W
最简单的映射之一是 linear map,它(在某些假设下**)总是可以用矩阵表示。矩阵的维数总是由我们正在处理的向量 spaces 的维数给出,因此从 R^N 到 R^M 的映射总是如下所示:
t: R^N ---> R^M
t(x) = A * x, A = R^(N,M)
其中A是N乘以M维的矩阵。
在 OpenGL 中,我们通常需要从 R^3 到 R^3 的映射,这意味着线性映射将始终由 3x3 矩阵表示。使用它,至少可以表达旋转、缩放(以及这***的组合)。但是在查看(例如)翻译时,我们发现无法使用 3x3 矩阵表示它们,因此我们必须扩展我们的转换以支持此操作。
这可以通过使用 affine mappings 而不是线性的来实现,线性的定义为
t: R^N ---> R^M
t(x) = A * x + b, A = R^(N,M) is a linear transformation and b = R^M
使用它,我们可以通过指定 3x3 矩阵和 3D 向量来表达从 R^3 到 R^3 的旋转、缩放和变换。由于这个公式不是很方便(需要一个矩阵和一个向量,难以组合多个变换),所以通常将操作存储在一个N+1维的矩阵中,称为增广矩阵(或增广向量space小号):
t: R^N ---> R^M
-A- b x
t(x) = [ ] * [ ]
-0- 1 1
如您所见,矩阵的最后一行始终为零,除了最右边的元素为 1。这也保证了结果 t(x) 的最后一个维度始终为 1。
Why is it necessary be strictly
[ 0, 0, 0, 1 ]
, why not just all the values be 0 or even some other value?
如果我们不将最后一行限制为恰好 [0,0,0,1]
,我们将不再在 R^3 中有增强仿射映射,而是在 R^4 中有线性映射。由于在 OpenGL 中 R^4 并不是真正相关的,而且我们希望保留翻译,所以最后一行是固定的。还有一点,当最后一行不同时,通过矩阵乘法组合仿射映射是行不通的。
剩下的一个问题是,我们仍然无法使用仿射映射来表达(透视)投影。在OpenGL中查看透视投影矩阵时,会注意到这里的最后一行不是[0,0,0,1]
,但背后的理论是完全不同的故事(如果有兴趣可以看看here or here) .
What's about the last row and why sometimes I see some calculations on it in libraries? The last one ( the homogeneous coordinate ) is modifying, so it could be not equal 1.0?
如前所述,最后一行仅 [0,0,0,1]
用于仿射映射,不适用于投影映射。但有时在投影后应用变换(例如在屏幕上移动投影图像)是有意义的,然后矩阵的最后一行必须得到尊重。这就是为什么大多数矩阵库以允许通用矩阵的方式实现所有操作的原因。行
out[15] = a03 * x + a13 * y + a23 * z + a[15];
只要最后一行 (a03, a13, a23, a[15]) 等于 [0,0,0,1]
.
因为这篇 post 已经比我想象的要长很多,我最好就此打住,但如果您有任何其他问题,请尽管提问,我会尝试在答案中添加一些内容。
脚注:
** 当两个 space 都是有限维向量 space 并且为它们定义了一个基础时有效。
*** 组合,因为有限维space上的线性变换的组合也是线性的,例如,t: R^N -> R^M, u: R^M -> R^K, 都是线性的 => t(u(x)) 线性的