您的位置:首页 > 移动开发 > IOS开发

iOS OpenGL ES 2.0教程 Lesson04--3D空间

2014-03-01 14:39 826 查看
/article/4128828.html

标签:OpenGL ES 教程 iOS tutorial




原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。/article/4128828.html

iOS OpenGL ES 2.0教程 Lesson04--3D空间

IOS Lesson 04 – 3D空间

在这一课里,我们使用三角形创建3D物体。我们将创建一个立方体,并让它旋转起来。

创建一个立方体
要创建一个立方体,需要指定8个顶点,绘画出前/后/左/右/上/下6个面。
首先修改程序Lesson03.mm:

//front
//vertex 0, left/buttom

geometryData.push_back(-0.5); geometryData.push_back(-0.5); geometryData.push_back(0.0); geometryData.push_back(1.0);

// 1, right/buttom
geometryData.push_back(0.5); geometryData.push_back(-0.5); geometryData.push_back(0.0); geometryData.push_back(1.0);

// 2, right/ups

geometryData.push_back(0.5); geometryData.push_back(0.5); geometryData.push_back(0.0); geometryData.push_back(1.0);

// 3, left/up

geometryData.push_back(-0.5); geometryData.push_back(0.5); geometryData.push_back(0.0); geometryData.push_back(1.0);

//back

//vertex 4, left/buttom

geometryData.push_back(-0.5); geometryData.push_back(-0.5); geometryData.push_back(-1.0); geometryData.push_back(1.0);

// 5, right/buttom

geometryData.push_back(0.5); geometryData.push_back(-0.5); geometryData.push_back(-1.0); geometryData.push_back(1.0);

// 6, right/ups
geometryData.push_back(0.5); geometryData.push_back(0.5); geometryData.push_back(-1.0); geometryData.push_back(1.0);

// 7, left/up

geometryData.push_back(-0.5); geometryData.push_back(0.5); geometryData.push_back(-1.0); geometryData.push_back(1.0);


指定8个顶点的坐标。顶点0-3的坐标不变,增加了顶点4-7,其坐标是分别将顶点0-3沿z轴负方向移动1个单位后得到的坐标值。

//3 floats define one color value (red, green and blue) with 0 no intensity and 1 full intensity

//each color triplet is assigned to the vertex at the same position in the buffer, so first color -> first vertex

// vertex 0 is blue

colorData.push_back(0.0); colorData.push_back(0.0); colorData.push_back(1.0);

// 1 is blue

colorData.push_back(0.0); colorData.push_back(0.0); colorData.push_back(1.0);

// 2 is blue

colorData.push_back(0.0); colorData.push_back(0.0); colorData.push_back(1.0);

// 3 is blue

colorData.push_back(0.0); colorData.push_back(0.0); colorData.push_back(1.0);

//4 is red

colorData.push_back(1.0); colorData.push_back(0.0); colorData.push_back(0.0);

//5 is red

colorData.push_back(1.0); colorData.push_back(0.0); colorData.push_back(0.0);

//6 is red

colorData.push_back(1.0); colorData.push_back(0.0); colorData.push_back(0.0);

//7 is red

colorData.push_back(1.0); colorData.push_back(0.0); colorData.push_back(0.0);

指定8个顶点的颜色。顶点0-3设定为蓝色,4-7设定为红色。

//create vertex indices

std::vector<GLubyte> indexData;

//front

indexData.push_back(0); indexData.push_back(1); indexData.push_back(2);

indexData.push_back(2); indexData.push_back(3); indexData.push_back(0);

//back

indexData.push_back(5); indexData.push_back(4); indexData.push_back(6);

indexData.push_back(4); indexData.push_back(7); indexData.push_back(6);

//top

indexData.push_back(3); indexData.push_back(2); indexData.push_back(6);

indexData.push_back(3); indexData.push_back(6); indexData.push_back(7);

//buttom

indexData.push_back(0); indexData.push_back(4); indexData.push_back(5);

indexData.push_back(0); indexData.push_back(5); indexData.push_back(1);

//left

indexData.push_back(0); indexData.push_back(3); indexData.push_back(4);

indexData.push_back(4); indexData.push_back(3); indexData.push_back(7);

//right

indexData.push_back(2); indexData.push_back(1); indexData.push_back(5);

indexData.push_back(2); indexData.push_back(5); indexData.push_back(6);

指定顶点索引数组。因为有6个面要画,需要指定12个三角形的顶点索引。

现在编译程序运行,就可以看到一个立方体在旋转了。

增加投影

现在,我们加入投影变换。投影变换的目的是定义一个视景体(viewing volume)。视景体的作用有两个,一是决定了一个物体是如何映射到屏幕上的(通过定义是投射投影还是正投影)。二是定义了物体的哪些部分会被裁减掉,不在最终的图像上显示。





我们在这里使用了透视投影。它的特点是物体离照相机越远,它在最终图像中看上去就越小。透视投影的视景体就好象是一个金字塔被截去了顶部的一块。越靠近观察点,视景体的截面大小就越小。所以同样大小的物体越靠近观察点,它就能在视景体中占据较大的区域,最终投影就越大。距离观察点越远的物体,最终投影就越小。
位于视景体之外的物体(或者物体的一部分)将会被裁剪掉,不会出现在最终的图像中,这点要特别注意。

增加一个投射投影,修改shader.vert文件:

//modelview projection matrix
uniform mat4 modelviewprojectionMatrix;

//the shader entry point is the main method

void main()
{
colorVarying = color; //save the color for the fragment shader

gl_Position = modelviewprojectionMatrix * position; //copy the position

}


我们将uniform变量改为 modelviewprojectionMatrix,这个矩阵包括了我们的投影变化和之前的modelview变换。

看Lesson03.mm中的 draw()函数:

//create mvp matrix

CC3GLMatrix *mvpMatrix = [CC3GLMatrix matrix];

//use perspective projection

[mvpMatrix populateFromFrustumLeft:-1 andRight:1 andBottom:-1 andTop:1 andNear:2 andFar:7];

//translate in z axis for -5

[mvpMatrix translateBy:CC3VectorMake(0, 0, -5)];

// rotate

m_currentRotation += 1.0;

[mvpMatrix rotateBy:CC3VectorMake(m_currentRotation, m_currentRotation, 0)];

//modify uniform variable

glUniformMatrix4fv(m_mvpMatrix, 1, 0, mvpMatrix.glMatrix);


首先,创建一个CC3GLMatrix的实例对象mvpMatrix。然后调用函数populateFromFrustum指定透视投影矩阵。函数创建一个平头截体(frustum),它的近侧裁减平面的左下角的坐标为(-1, -1, -2),右上角的坐标为(1, 1, -2)。从观察点到近侧和远侧裁减平面的距离分别是2和7。

然后translateBy设置顶点移动的数据,这里是沿z轴负方向移动5,x,y轴方向不变。目的是为了让整个物体在旋转过程中始终可以保证在视景体的内部,不会有被裁减的部分。
roteteBy函数提供了旋转数据,我们让立方体同时沿x 轴和y轴旋转。
最后,调用glUniformMatrix4vf函数用我们的变换矩阵来修改着色器程序里的uniform变量 modelviewprojectionMatrix的数值。

现在编译并运行程序,会发现旋转的立方体的尺寸变小了。这是因为投射投影的关系,我们将物体沿z轴负方向移动了5,物体现在处于视景体的比较远的位置,最后投影到屏幕上的尺寸就相应的变小了。

增加深度测试

我们先将Lesson03.mm中,init()方法中的函数glEnable(GL_CULL_FACE) 注释掉,然后编译并运行程序,看看会出现什么现象。
你会发现,可以看到立方体里面的面了!

这是因为,执行glEnable(GL_CULL_FACE)的话,OpenGL会根据顶点的指定顺序,将背对我们的表面(即顶点按照顺时针方向指定)裁剪掉,它们不会显示出来。
如果将它注释掉了,不管是正对我们的表面还是背对我们的表面都会显示出来,又因为我们没有做深度测试,就出现了刚刚的现象了。

我们接下来加上深度测试(depth testing)。启动它以后,OpenGL会跟踪在z轴上的像素。深度缓冲区负责记录观察点和占据这个像素的物体间的距离。在整个场景被渲染时,只有没有被其他物体(像素)遮挡的物体(像素)才会被保留,最终被绘制出来。

首先我们看Lesson03AppDelegate.mm函数,允许增加深度缓存

[glview setDepthBufferNeeded:TRUE]


则在EAGLView.mm 的createFramebuffer函数中,下面的代码就被执行:

if(useDepthBuffer)

{

//create a depth renderbuffer
glGenRenderbuffers(1, &depthRenderbuffer);

glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);

//create the storage for the buffer, optimized for depth values, same size as the colorRenderbuffer

glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, framebufferWidth, framebufferHeight);

//attach the depth buffer to our framebuffer

glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);

}

首先创建了一个depthRenderbuffer,然后将它绑定到GL_RENDERBUFFER。
我们用glRenderbufferStorage来创建并初始化renderbuffer对象的数据存储空间。第一个参数必须是GL_RENDERBUFFER,第二个参数指定了depth renderbuffer的数据格式。后两个参数是depth renderbuffer的宽度和高度,和之前的其他buffer一样。
最后,调用glFramebufferRenderbuffer,将我们创建的depth buffer关联到framebuffer上。

最后,我们回到Lesson03.mm:

//drawing a frame
void Lesson03::draw()
{
//clear the color buffer

glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);

glEnable(GL_DEPTH_TEST);

在每一帧的开始,都清除深度缓存glClear(GL_DEPTH_BUFFER_BIT),并启用深度测试。
编译并运行程序,看看最后的效果。





附件里有本教程的完整源代码。

本文出自 “周晶的博客” 博客,请务必保留此出处/article/4128828.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: