原创文章,转载请注明连接 http://blog.csdn.net/hoytgm/article/details/37911805
这章介绍一下透视投影变换,一个物体经过了透视投影变换之后,就会有近大远小的效果,这很符合我们日常观察的结果。
在这之前,我们需要创建一个立体的模型,这样才有透视投影的效果,像之前只画一个三角形或者正方形是不行的。立方体的顶点和Index数组如下
glGenBuffers(2, buffers);
glBindBuffer(GL_ARRAY_BUFFER, buffers[VERTEX]);
float cube[] =
{
-0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.5f, 0.5f, 1.0f,
};
glBufferData(GL_ARRAY_BUFFER, sizeof(cube), cube, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[INDEX]);
static unsigned short indices[] =
{
/* 0 */
0, 1, 2, 1, 2, 3,
0, 4, 2, 6, 4, 2,
0, 1, 4, 1, 5, 4,
1, 3, 5, 3, 5, 7,
2, 3, 6, 3, 6, 7,
4, 5, 6, 5, 6, 7,
};
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
为了便于区分每个面,我们给每个顶点加了一个颜色的属性,所以,Shader也需要做相应的变化来应用新加的颜色属性
char* vssource =
"precision mediump float;\n"
"attribute vec4 aPosition;\n"
"attribute vec3 aColor;\n"
"uniform mat4 uMvp;\n"
"varying vec3 vColor;\n"
"void main() {\n"
" gl_Position = uMvp*aPosition;\n"
" vColor = aColor;\n"
"}\n";
char* fssource =
"precision mediump float;\n"
"varying vec3 vColor;\n"
"void main() {\n"
" gl_FragColor = vec4(vColor, 1.0);\n"
"}\n";
因为多了一个属性,所以需要再程序里面去得到Shader中的句柄,并启用它
aLocColor = glGetAttribLocation(program, "aColor");
glEnableVertexAttribArray(aLocColor);
而且在设置顶点的属性的时候,我们需要注意glVertexAttribPointer函数中的两个参数,Stride和offset,也就是最后两个参数。因为在之前只有一个顶点位置的属性,所以Stride为0,而现在一个Vertex Buffer里面同时有了顶点和颜色两个属性,所以需要指定Stride为多大。一个Stride就是指一组顶点加颜色占用了多少Byte,程序中一组顶点有4个float,一组颜色有3个float,所以这里的Stride就是sizeof(float)*7。而最后一个参数,在没有用Vertex Buffer的时候,是顶点数组的指针,如果我们用了Vertex Buffer,最后一个参数就是Offset,它是指当前属性在一个Stride中需要偏移多少个Byte。一组顶点和颜色的Stride中,顶点是起始,所以offset为0,而颜色是在顶点结束后才开始,所以Offset是sizeof(float)*4。
glVertexAttribPointer(aLocPos, 4, GL_FLOAT, 0, sizeof(float)*7, 0);
glVertexAttribPointer(aLocColor, 3, GL_FLOAT, 0, sizeof(float)*7, (GLvoid*)(sizeof(float)*4));
运行程序
现在只得到了一个有颜色矩形。为什么???因为我们现在还没有用透视投影变换,所以还处于一个正投影的世界中,这样的话,远近是看不出来的,这时候需要构造两个矩阵,第一个矩阵是决定了我们在哪里观察物体,像眼睛的位置,向上的方向是什么,观看点在哪里,这个就是观察矩阵(View)。在这里,把眼睛的位置放到(0, 0, -2),就是相当于退后2个单位,而观察点在(0, 0, 0),就是一直观看坐标中心。
gmMatrix4 view, proj;
gmVector3 eye = {0.0f, 0.0f, -2.0f};
gmVector3 at = {0.0f, 0.0f, 0.0f};
gmVector3 up = {0.0f, 1.0f, 0.0f};
gmMatrixLookAtLH(&view, &eye, &at, &up);
第二个矩阵决定了我们的视野大小,高宽比,最近能看到哪里,最远能看到哪里等属性,也就是投影矩阵(project)
gmMatrixPerspectiveFovLH(&proj, 3.1415f / 2, (float)_viewWidth / (float)_viewHeight, 1.0f, 1000.0f);
这两个矩阵的构造函数在之前的代码里面已经添加上去了,但是没有使用过。大家会注意到两个函数后面都有LH的字样,这个是指Left Hand,也就是左手坐标系。关于怎么区分左手或右手坐标系,这个可以查一下,需要指明的是,OpenGL和OpenGLES中,都是使用左手坐标系,而在Direct3D中却使用的是右手坐标系。
计算出两个矩阵后,将他们相乘,就是我们要的mvp了
gmMatrixMultiply(&mvp, &view, &proj);
再运行程序
好像除了比刚才小一点之外,没有什么变化呀,改改eye的位置,把y往上面移动两个单位,再运行一下
gmVector3 eye = {0.0f, 2.0f, -2.0f};
有点感觉了吧?再把x往右移两个单位看看
gmVector3 eye = {2.0f, 2.0f, -2.0f};
怎么样,像一个立方体了吧???我们再加一段旋转的代码,这是之前做过的
gmMatrix4 model, view, proj;
static float angle = 0.0f;
gmMatrixRotateY(&model, angle);
angle += 0.1f;
现在我们有了三个矩阵,注意一下相乘的顺序,既然是mvp,那么相乘的顺序就是model * view * project
gmMatrixMultiply(&mvp, &model, &view);
gmMatrixMultiply(&mvp, &mvp, &proj);
再来看看结果,非常漂亮~~~~
代码地址http://download.csdn.net/detail/hoytgm/7654435