[OpenGL]矩阵乘法引发的血案
2017-03-12 14:33
691 查看
最近被矩阵乘法折腾的死去活来,感觉要打回去重新学线代了。
看起来好像挺简单,就是把左边的矩阵和右边的矩阵乘起来放到结果里面去,但是之前说到Matrix是列主序的,那乘法到底是怎么操作的呢?
我们知道常规的行主序的矩阵乘法,结果的第一行第一列的元素是由lhs的第一行和rhs第一列相乘累加得到的,那既然这个矩阵是列主序的,是不是lhs的第一列和rhs的第一行呢?
这是个native函数,直接上代码:
看完代码以后,我发现这个乘法还是lhs的第一行和rhs第一列呀。
貌似列主序只是存取的方式有变化,矩阵还是那个矩阵,乘法还是那个乘法。
Android的Matrix(OpenGL)类中有一段话,说虽然乘法的结果是lhs x rhs,但是结果相当于先乘上rhs,再乘上lhs
我们使用glsl并且在Java层传递MVP矩阵的时候,乘法顺序是这样的:
相当于对于一个顶点(用列主序向量表示),先左乘model转换成世界坐标,再左乘view转换成眼坐标,再左乘projection转换成裁剪坐标
对于modelMatrix,平移操作在旋转操作之前,因为旋转后参考的坐标轴也会发生改变
但是viewMatrix刚好相反,是先旋转再平移因为相机的姿态变化相当于世界坐标的反向变化,如果对viewMatrix取逆,那么就相当于求出了相机(想象的概念)在世界坐标系下面的位置和姿态。
但是使用gl原生接口(OpenGL1.0时),是这样子的
执行glMultMatrixf(M)以后,相当于当前的矩阵C=V变成了C=M*V 。虽然代码执行顺序刚好相反,但是结果是一样的。
结果是什么呢?应该是一个这样的矩阵:
如果用这个矩阵左乘上当前矩阵M,相当于翻转当前矩阵的第二行和第三行,如果是右乘,则是翻转第二列和第三列。
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,相当于翻转当前矩阵的第二行和第三行,如果是右乘,则是翻转第二列和第三列。
相关文章推荐
- OpenGL学习进程(12)第九课:矩阵乘法实现3D变换
- BZOJ 1875 [SDOI2009]HH去散步 矩阵乘法
- C语言 · 矩阵乘法
- qsort还是std::sort,一个排序引发的血案
- Android 4.0 中由ProGuard引发的一场血案
- 转发同事总结:一个BUG引发的血案(结果篇)
- Android 4.0 中由ProGuard引发的一场血案
- (原)一句mpAudioPolicy->get_input引发的血案
- 一个Assert引发的血案
- netty客户端引发的线程血案(三)
- 一个Php的Xml库的Bug引发的血案
- 矩阵的运算的乘法次数(栈的模拟应用)
- Java程序实现矩阵乘法
- 记Task Scheduler一个选项引发的血案
- 十个利用矩阵乘法解决的经典题目
- 【POJ 3233】【二分+矩阵乘法】Matrix Power Series【求S = A + A2 + A3 + … + Ak】
- 条件断点引发的血案
- 一个由正则表达式引发的血案
- UVa10681 - Teobaldo's Trip(矩阵乘法、包传递)
- OpenGL进阶(二) - 自定义矩阵加载