1

从以下来源android.opengl.Matrix.translateM(float[] m, int mOffset, float x, float y, float z)

/**
 * Translates matrix m by x, y, and z in place.
 * @param m matrix
 * @param mOffset index into m where the matrix starts
 * @param x translation factor x
 * @param y translation factor y
 * @param z translation factor z
 */
public static void translateM(
        float[] m, int mOffset,
        float x, float y, float z) {
    for (int i=0 ; i<4 ; i++) {
        int mi = mOffset + i;
        m[12 + mi] += m[mi] * x + m[4 + mi] * y + m[8 + mi] * z;
    }
}

问题是关于这一行(或整个 for 循环):

m[12 + mi] += m[mi] * x + m[4 + mi] * y + m[8 + mi] * z;

为什么翻译参数的 x、y 和 z 分量在这里与源矩阵分量相乘?这不应该很简单吗(这行替换了整个 for 循环):

m[12 + mi] += x;
m[13 + mi] += y;
m[14 + mi] += z;

问题背景

我在 OpenGL ES 2.0 中做一些 2d 游戏。我想在那里缩放和移动一些对象。虽然我简单地移动它:

Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, x, y, 0);

一切顺利。只要我在移动前进行缩放 - 此时移动翻译:

Matrix.setIdentityM(mModelMatrix, 0);
Matrix.scaleM(mModelMatrix, 0, xScaleFactor, yScaleFactor, 1);
Matrix.translateM(mModelMatrix, 0, x, y, 0);

实际上乘以缩放:(

4

2 回答 2

3

OpenGL 是列优先的,缩放、旋转和平移的正确顺序实际上是Translation * Rotation * Scaling. 在 D3D 和任何行主矩阵库中,Scale * Rotate * Translate都是正确的。当你使用列优先矩阵时,你必须从右到左考虑事情。

或者,您可以在乘法之前对每个矩阵进行转置——尽管遵循列主矩阵乘法的规范顺序通常更简单。请注意,这也适用于Position * Model * View * Projection(D3D / row-major) 之类的东西,在 GL (column-major) 中,正确的顺序是Projection * View * Model * Position.

于 2013-11-17T22:08:05.420 回答
0

translateM 模拟mX的矩阵乘法T,其中T是平移矩阵。

平移矩阵就像一个单位矩阵(对角线 1),最后一列 x,y,z,1用于平移。(1是我不会进入的同质坐标)。

假设您知道矩阵乘法(其中第一个操作数的每一行与第二个操作数的每一列成对相乘),它通过利用平移矩阵的细节来走捷径T

它实际上并没有创建平移矩阵T,而是模拟了乘法m x T。大多数值不会改变(因为像单位矩阵这样的部分),所以它只需要计算确实改变的值(涉及 的最后一列T)。

每个循环迭代成对地将一行乘以m平移矩阵的最后一列(即x,y,z,1)。代码行将是(其中i是 的行m和列T):

m[12+i] = m[0+i]*x + m[4+i]*y + m[8+i]*z + m[12+i]*1f;

但是他们用+=操作员简化了它;用于mi偏移;并省略0+

不改变的元素可以留在那里,因为这个版本translateM是“就地”的。

请记住,它使用列主要形式的矩阵,带有索引:

0  4  8  12
1  5  9  13
2  6  10 14
3  7  11 15

当您逐步遍历数组时,您会向下遍历列中的每个元素,然后在其末尾遍历下一列,依此类推。因此,第i行和第j列的元素位于m[i + 4*j]。也就是说,往下走是+1,往上走是+4。thr next (另一种方式是row-major,您可以在其中穿过一行,向下到下一行等。对于打印来说更直观,但不是什么android.opengl.Matrix用途。)


您的 scaleMthentranslateM相当于S x T(缩放矩阵S,平移矩阵T)。效果是从右到左(即向后),首先是平移,然后是缩放。因此,翻译也得到了缩放。

正如公认的答案所说,做translateMthen scaleM(ie T x S) 具有缩放然后平移的效果(因此平移没有缩放)。

考虑这一点的一种方法是,该矩阵最终将乘以列向量v: (T x S) x v。以不同的方式开始,使用S x v = v'scaling v,然后将结果转换为T x v',我们可以将它们组合为T x (S x v)。缩放必须是第一个似乎很自然。也许令人惊讶,因为矩阵乘法是关联的,它与 相同(T x S) x v。如果我们忽略最后一个x v(在 GPU 上完成),我们有T x S,这意味着“缩放,然后翻译”。最靠近右边的矩阵首先完成。

顺便说一句,矩阵乘法的关联性就像 java 字符串连接,其中评估的顺序无关紧要,在哪里(a+b)+c = a+(b+c)(尽管操作数的顺序确实很重要,例如a+b+c != c+b+a- 两者都不是commutative)。这意味着我们可以先选择适合我们的部分。


顺便说一句:这里有几层:矩阵乘法,排序效果,齐次坐标,平移矩阵,乘法排序效果,列主要形式,上述乘法快捷方式+=,和mOffset,mi`(对于一个数组中的许多矩阵)。

很多编程可以随着你的进行而理解,但为此,最好依次了解每一层。

于 2020-03-13T05:34:07.387 回答