您的位置:首页 > 运维架构

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

2017-03-12 14:33 691 查看
最近被矩阵乘法折腾的死去活来,感觉要打回去重新学线代了。

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 * T
Matrix.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.0
0.0 -1.0 0.0 0.0
0.0 0.0 -1.0 0.0
0.0 0.0 0.0 1.0


如果用这个矩阵左乘上当前矩阵M,相当于翻转当前矩阵的第二行和第三行,如果是右乘,则是翻转第二列和第三列。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: