原创文章,转载请注明链接 http://blog.csdn.net/hoytgm/article/details/37670893
上一章的Pipe Line中,我们讲了坐标变换,就是将开发者输入的点进行世界(model)、视点(view)和投影(perspective)变换之后,全部转化到一个截锥体(Frustum)中,然后做下一步工作。我们可以在Vertex Shader中将model、view和perspective变换做到一个4x4的矩阵中,这就是mvp矩阵,也就是我们这章要讲的矩阵变换。这部分有一些矩阵的算法,如果想明白里面的计算方式,可能需要知道一些线性代数的知识。不过不知道也没关系,知道怎么用这些函数就可以啦~~
我们先说说图形学中的点吧,一个点通常由(x, y, z, w)四个元素构成。三维空间中的位置是由x, y, z组成,为什么这里还有一个w元素呢?其实,w是一个其次坐标。我们上一章说过在裁剪和剔除环节,裁掉大部分点,而剩下的这些点,有一个共同的特性,就是 -w<x<w, -w<y<w, -w<z<w。在x,y,z进行矩阵变换之后,会把Frustum中的所有的点都进行归一化,那就是把x, y, z除以w。前面的公式除以w之后,我们就得到了-1<(x/w)<1, -1<(y/w)<1, -1<(z/w)<1。看,在(-1, 1)之间就是一个归一化的矩阵咯。
我们再说说矩阵吧,图形学用到的矩阵是一个4x4的矩阵,就是
其中,一个单位矩阵是指左对角线上的元素全为1,其他元素全为0的矩阵
我们用一个单位矩阵去乘以我们的一个点坐标(x, y, z, w),就可以得到如下结果
这样可以看出,其实一个单位矩阵对一个点坐标位置并没有任何影响。
我们在代码中定义一个三维向量和一个4x4的矩阵结构体,为了方便移植到Android上面,我们这里都用c语言完成
typedef struct _gmVector3
{
float x;
float y;
float z;
}
gmVector3;
typedef struct _gmMatrix4
{
float m[16];
}
gmMatrix4;
然后我们写一个函数,讲其中的三个元素改变值
void gmMatrixTranslate(gmMatrix4* out, float x, float y, float z)
{
if (out == NULL)
{
return;
}
out->m[ 0]=1.0f; out->m[ 4]=0.0f; out->m[ 8]=0.0f; out->m[12]=x;
out->m[ 1]=0.0f; out->m[ 5]=1.0f; out->m[ 9]=0.0f; out->m[13]=y;
out->m[ 2]=0.0f; out->m[ 6]=0.0f; out->m[10]=1.0f; out->m[14]=z;
out->m[ 3]=0.0f; out->m[ 7]=0.0f; out->m[11]=0.0f; out->m[15]=1.0f;
}
在单位矩阵的基础上将m12, m13和m14分别赋予了x, y, z,用这样一个矩阵再来乘以我们的一个点(x, y, z, w)有什么结果?
看,原来的点坐标上面分别加上了一个偏移,这就是这个矩阵的作用,做了一个Translate。
再写一个函数看看
void gmMatrixScale(gmMatrix4* out, float x, float y, float z)
{
if (out == NULL)
{
return;
}
out->m[ 0]=x; out->m[ 4]=0.0f; out->m[ 8]=0.0f; out->m[12]=0.0f;
out->m[ 1]=0.0f; out->m[ 5]=y; out->m[ 9]=0.0f; out->m[13]=0.0f;
out->m[ 2]=0.0f; out->m[ 6]=0.0f; out->m[10]=z; out->m[14]=0.0f;
out->m[ 3]=0.0f; out->m[ 7]=0.0f; out->m[11]=0.0f; out->m[15]=1.0f;
}
我们讲对角线上的值改变了,那他的作用呢?
原来的点各乘以了一个值,相当于做了放大或缩小,这就是Scale。
有了放大缩小和偏移,我们还可以做旋转。不过旋转稍稍复杂点,因为要考虑绕哪一个轴旋转。我们可以写一个绕Y轴旋转的函数
void gmMatrixRotateY(gmMatrix4* out, float y)
{
if (out == NULL)
{
return;
}
float fcos, fsin;
fcos = cos(y);
fsin = sin(y);
out->m[ 0]=fcos; out->m[ 4]=0.0f; out->m[ 8]=-fsin; out->m[12]=0.0f;
out->m[ 1]=0.0f; out->m[ 5]=1.0f; out->m[ 9]=0.0f; out->m[13]=0.0f;
out->m[ 2]=fsin; out->m[ 6]=0.0f; out->m[10]=fcos; out->m[14]=0.0f;
out->m[ 3]=0.0f; out->m[ 7]=0.0f; out->m[11]=0.0f; out->m[15]=1.0f;
}
这个矩阵的效果???一会我们就看看。
我们这里写了一些函数和结构体,更多详细的代码大家可以去我的资源页下载,我也会在最后贴出代码地址哈~~~
讲了这么多,回到代码中去吧。要用这个矩阵,首先需要修改一下之前的Vertex Shader程序
"precision mediump float;\n"
"attribute vec4 aPosition;\n"
"uniform mat4 uMvp;\n"
"void main() {\n"
" gl_Position = uMvp*aPosition;\n"
"}\n";
这里多用了一个uniform叫做uMvp,mat4表明这是一个4x4的矩阵。
同样,需要得到这个uniform的地址
uLocMvp = glGetUniformLocation(program, "uMvp");
对这个uniform赋值需要用到glUniformMatrix4fv,这就是传一个矩阵到shader里面。
再定义一个4x4的矩阵并初始化它
gmMatrix4 mvp;
InitgmMatrix4(&mvp);
然后,运行程序,画一个红色的三角形,它的坐标为
float firsttriangle[] =
{
-0.8f, -0.8f, 0.0f, 1.0f,
0.8f, -0.8f, 0.0f, 1.0f,
0.0f, 0.5f, 0.0f, 1.0f,
};
得到的结果
和以前没什么区别吧??
我们加一段代码,做一个Translate,看看结果
gmMatrixTranslate(&mvp, 0.5, 0.0, 0.0);
哈哈,向右边偏移了一点吧?因为我们刚才传的参数是(0.5, 0, 0),也就是y和z不变,x右移一点,很符合结果
改成Scale试试呢?
gmMatrixScale(&mvp, 0.5, 0.5, 0.5);
变小了吧?因为参数是0.5,相当于乘以0.5就是1/2,原来的一半了。
又改成Rotate绕Y轴呢?
static float angle = 0.0f;
gmMatrixRotateY(&mvp, angle);
angle += 0.1f;
开始旋转啦!!!我们的三角形终于开始动起来咯
好咯,矩阵变换就先讲这么多,这一章的代码很重要,欢迎大家去地址 http://download.csdn.net/detail/hoytgm/7623267 下载。
注:本文参考了PVR SDK