在opengl中,默认世界坐标系已经定位好了,要绘制图形,给出图形的顶点坐标参数很多情况下是按照世界坐标系设定各自顶点坐标.
比如,下面给出了一个立方体的各自顶点坐标,那么下面是按照世界坐标系给出的数据参数.
final float cubePosition[] =
{
// Front face
-1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
// Right face
1.0f, 1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
1.0f, 1.0f, -1.0f,
1.0f, -1.0f, 1.0f,
1.0f, -1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
// Back face
1.0f, 1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
// Left face
-1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
// Top face
-1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f,
// Bottom face
1.0f, -1.0f, -1.0f,
1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, -1.0f,
1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, -1.0f,
};
这样建立的给出的立方体,矩阵为M,那么如果想对这个立方体对象进行平移,转换,旋转等操作,就需要提供一个变换矩阵N(Model View),那么M经过N的变换后就得到新的显示位置,在数学上,变换后W=M*N,在程序中:
gl_Position=u_MvpMatrix*a_Position;
其中u_MvpMatrix是(Model View)变换矩阵,a_Position为最前面的那个立方体坐标,两者相乘就得到新的坐标位置.
那么根据标题,如果要调整立方体的位置,那么就要赋给Model View对应的向量变化,而Model View一般会被初始化为单位正交矩阵:
下面需要注意一点,矩阵运算返回值都是保存在下面方法的第一个参数数组中.
Matrix.setIdentityM(mModelMatrix, 0);
其中mModelMatrix是一个float[4*4]的数组来表示矩阵.通俗的讲就是给这个赋一个初始值,只是这个初始值是一种矩阵式的初始值.任何其他矩阵乘以该矩阵都等于乘以数(乘以矩阵).
比如在上面基础上,在Z轴平移-5个单位:
Matrix.translateM(mLightModelMatrix, 0, 0.0f, 0.0f, -5.0f);
那么就如下:
这个地方要注意,opengl转换的矩阵形式如下:
上面m的下标对应mModelMatrix数组的索引,所以仔细看,12,13,14,15在最右边一列,而不是最下一行,说面数组是按照列的形式进行保存的,这一点千万要注意,不然按照正常的人工手头去算,算到死都会和程序不一样.
Matrix.rotateM(mModelMatrix, 0, angleInDegrees, 1.0f, 1.0f, 0.0f);
将前面的位置按照矢量(1.0,1.0,0.0)旋转angleInDegrees角度,这个角度是360度制的,即选择45度,就输入45度.这个rotateM方法里面运算还是比较麻烦的,所以不像平移,这个方法的具体源代码在jni层,是用c写的.
Matrix.multiplyMM (float[] result, int resultOffset, float[] lhs, int lhsOffset, float[] rhs, int rhsOffset);
将两个4x4矩阵相乘,并将结果存储在第三个4x4矩阵中。以矩阵表示法表示:result=lhs x rhs。
例如:使用OpenGL ES 2.0旋转绘制对象。先创建另一个变换矩阵(一个旋转矩阵),然后将它合并到投影和相机视图变换矩阵就可以了:
private float[] mRotationMatrix = new float[16];
public void onDrawFrame(GL10 gl) {
...
// 为三角形创建一个旋转变换
long time = SystemClock.uptimeMillis() % 4000L;
float angle = 0.090f * ((int) time);
Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f);
// 将旋转矩阵合并到投影和相机视图矩阵
Matrix.multiplyMM(mMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0);
// 绘制三角形
mTriangle.draw(mMVPMatrix);
}
我们还有一种multiplyMV是怎么回事呢?
Matrix.setIdentityM(mLightModelMatrix, 0);
Matrix.translateM(mLightModelMatrix, 0, 0.0f, 0.0f, -5.0f);
//Matrix.rotateM(mLightModelMatrix, 0, angleInDegrees, 0.0f, 1.0f, 0.0f);
Matrix.translateM(mLightModelMatrix, 0, 0.0f, 0.0f, 2.0f);
Matrix.multiplyMV(mLightPosInWorldSpace, 0, mLightModelMatrix, 0, mLightPosInModelSpace, 0);
Matrix.multiplyMV(mLightPosInEyeSpace, 0, mViewMatrix, 0, mLightPosInWorldSpace, 0);
这段代码是设置光源的,我们只看最后两行,其中参数:
private final float[] mLightPosInModelSpace = new float[] {0.0f, 0.0f, 0.0f, 1.0f};
private final float[] mLightPosInWorldSpace = new float[4];
private final float[] mLightPosInEyeSpace = new float[4];
从上面可以看出是一个1*4的矩阵,而:
private float[] mLightModelMatrix = new float[16];
是4*4的矩阵,那么就很显然,这个multiplyMV就是将一个4*4的矩阵与一个1*4的矩阵相乘,最后得到一个1*4的矩阵.
倒数第二行程序的意思是将光源坐标mLightPosInModelSpace经过mLightModelMatrix的转换,得到最终在世界坐标系中的坐标位置.
但是由于上面经过转换以后得到了坐标,但是有位置并代表就可以被观察者(camera)看到,那么就要继续讲这个坐标再进行View Model变换,从而置于观察者视窗下:
Matrix.multiplyMV(mLightPosInEyeSpace, 0, mViewMatrix, 0, mLightPosInWorldSpace, 0);
当然上面是想看到光源位置可以加上.
这个转换很繁琐,主要要搞清楚View,model,已经变换后的位置,可以到网上下载模拟器工具手工设置一下就明白了.