[OpenGL]矩阵乘法引发的血案

来源:互联网 发布:大数据与软件工程 编辑:程序博客网 时间:2024/06/02 00:48

最近被矩阵乘法折腾的死去活来,感觉要打回去重新学线代了。

Matrix定义

OpenGL中Matrix被定义成一个列主序的矩阵,大小为3x3或者4x4

/** * Matrix math utilities. These methods operate on OpenGL ES format * matrices and vectors stored in float arrays. * <p> * Matrices are 4 x 4 column-vector matrices stored in column-major * order: * <pre> *  m[offset +  0] m[offset +  4] m[offset +  8] m[offset + 12] *  m[offset +  1] m[offset +  5] m[offset +  9] m[offset + 13] *  m[offset +  2] m[offset +  6] m[offset + 10] m[offset + 14] *  m[offset +  3] m[offset +  7] m[offset + 11] m[offset + 15]</pre> * * Vectors are 4 x 1 column vectors stored in order: * <pre> * v[offset + 0] * v[offset + 1] * v[offset + 2] * v[offset + 3]</pre> */

Matrix.multiplyMM在干什么

函数原型:

public static native void multiplyMM(float[] result, int resultOffset,            float[] lhs, int lhsOffset, float[] rhs, int rhsOffset);

看起来好像挺简单,就是把左边的矩阵和右边的矩阵乘起来放到结果里面去,但是之前说到Matrix是列主序的,那乘法到底是怎么操作的呢?

我们知道常规的行主序的矩阵乘法,结果的第一行第一列的元素是由lhs的第一行和rhs第一列相乘累加得到的,那既然这个矩阵是列主序的,是不是lhs的第一列和rhs的第一行呢?

这是个native函数,直接上代码:

#define I(_i, _j) ((_j)+ 4*(_i))float sTemp[16];void multiplyMM(float* r, const float* lhs, const float* rhs) {    for (int i=0 ; i<4 ; i++) {        register const float rhs_i0 = rhs[ I(i,0) ];        register float ri0 = lhs[ I(0,0) ] * rhs_i0;        register float ri1 = lhs[ I(0,1) ] * rhs_i0;        register float ri2 = lhs[ I(0,2) ] * rhs_i0;        register float ri3 = lhs[ I(0,3) ] * rhs_i0;        for (int j=1 ; j<4 ; j++) {            register const float rhs_ij = rhs[ I(i,j) ];            ri0 += lhs[ I(j,0) ] * rhs_ij;            ri1 += lhs[ I(j,1) ] * rhs_ij;            ri2 += lhs[ I(j,2) ] * rhs_ij;            ri3 += lhs[ I(j,3) ] * rhs_ij;        }        r[ I(i,0) ] = ri0;        r[ I(i,1) ] = ri1;        r[ I(i,2) ] = ri2;        r[ I(i,3) ] = ri3;    }}

看完代码以后,我发现这个乘法还是lhs的第一行和rhs第一列呀。
貌似列主序只是存取的方式有变化,矩阵还是那个矩阵,乘法还是那个乘法。

OpenGL矩阵乘法顺序

 * Multiplies two 4x4 matrices together and stores the result in a third 4x4 * matrix. In matrix notation: result = lhs x rhs. Due to the way * matrix multiplication works, the result matrix will have the same * effect as first multiplying by the rhs matrix, then multiplying by * the lhs matrix. This is the opposite of what you might expect.

Android的Matrix(OpenGL)类中有一段话,说虽然乘法的结果是lhs x rhs,但是结果相当于先乘上rhs,再乘上lhs

我们使用glsl并且在Java层传递MVP矩阵的时候,乘法顺序是这样的:

//P * V * M * TMatrix.multiplyMM(modelViewMatrix, 0, viewMatrix, 0, modelMatrix, 0);Matrix.multiplyMM(mMVPMatrix, 0, projectionMatrix, 0, modelViewMatrix, 0);

相当于对于一个顶点(用列主序向量表示),先左乘model转换成世界坐标,再左乘view转换成眼坐标,再左乘projection转换成裁剪坐标

对于modelMatrix,平移操作在旋转操作之前,因为旋转后参考的坐标轴也会发生改变

但是viewMatrix刚好相反,是先旋转再平移因为相机的姿态变化相当于世界坐标的反向变化,如果对viewMatrix取逆,那么就相当于求出了相机(想象的概念)在世界坐标系下面的位置和姿态。

但是使用gl原生接口(OpenGL1.0时),是这样子的

glMatrixMode(GL_PROJECTION);glLoadMatrixf(projectionM);glMatrixMode(GL_MODELVIEW);glLoadMatrixf(viewM);glMultMatrixf(modelM);//draw points

执行glMultMatrixf(M)以后,相当于当前的矩阵C=V变成了C=M*V 。虽然代码执行顺序刚好相反,但是结果是一样的。

坐标系重映射

有些时候我们需要将一个坐标系映射到另外一个坐标系,先来看下面的代码

Matrix.setIdentityM(tmpMatrix,0);Matrix.rotateM(tmpMatrix,0, +180.0f, 1.0f, 0.0f, 0.0f);

结果是什么呢?应该是一个这样的矩阵:

1.0 0.0 0.0 0.00.0 -1.0 0.0 0.00.0 0.0 -1.0 0.00.0 0.0 0.0 1.0

如果用这个矩阵左乘上当前矩阵M,相当于翻转当前矩阵的第二行和第三行,如果是右乘,则是翻转第二列和第三列。

0 0
原创粉丝点击