上篇文章中,我们介绍了VBO索引的使用,使用VBO索引可以有效地减少顶点个数,优化内存,提高程序效率。

本教程将带领大家一起走进3D–绘制一个立方体。其实画立方体本质上和画三角形没什么区别,所有的模型最终都要转换为三角形。

同时,本文还会介绍如何通过修改MVP矩阵来让此立方体不停地旋转。另外,大家还可以动手去修改本教程的示例代码,借此我们可以更加深入地理解OpenGL的normalized device space。

准备立方体数据

在开始真正的绘制代码之前,我们先要准备好数据。首先,我们需要改进的是代表顶点属性的结构体:

1
2
3
4
 typedef struct {
float Position[3];
float Color[4];
} Vertex;

这里,我们把Position从一个长度为2的数组变成了一个长度为3的数组,用于存储顶点的xyz的值。

接下来是顶点数据,因为一共有6个面。每个面由二个三角形组成,因此需要4个顶点,那么整个立方体就需要4*6=24个顶点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Vertex data[] =
{
// Front
{ {1, -1, 0}, {1, 0, 0, 1}},
{ {1, 1, 0}, {0, 1, 0, 1}},
{ {-1, 1, 0}, {0, 0, 1, 1}},
{ {-1, -1, 0}, {0, 0, 0, 1}},
// Back
{ {1, 1, -2}, {1, 0, 0, 1}},
{ {-1, -1, -2}, {0, 1, 0, 1}},
{ {1, -1, -2}, {0, 0, 1, 1}},
{ {-1, 1, -2}, {0, 0, 0, 1}},
// Left
{ {-1, -1, 0}, {1, 0, 0, 1}},
{ {-1, 1, 0}, {0, 1, 0, 1}},
{ {-1, 1, -2}, {0, 0, 1, 1}},
{ {-1, -1, -2}, {0, 0, 0, 1}},
// Right
{ {1, -1, -2}, {1, 0, 0, 1}},
{ {1, 1, -2}, {0, 1, 0, 1}},
{ {1, 1, 0}, {0, 0, 1, 1}},
{ {1, -1, 0}, {0, 0, 0, 1}},
// Top
{ {1, 1, 0}, {1, 0, 0, 1}},
{ {1, 1, -2}, {0, 1, 0, 1}},
{ {-1, 1, -2}, {0, 0, 1, 1}},
{ {-1, 1, 0}, {0, 0, 0, 1}},
// Bottom
{ {1, -1, -2}, {1, 0, 0, 1}},
{ {1, -1, 0}, {0, 1, 0, 1}},
{ {-1, -1, 0}, {0, 0, 1, 1}},
{ {-1, -1, -2}, {0, 0, 0, 1}}
};

接下来,当然是最重要的VBO索引啦:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
GLubyte indices[] = {
// Front
0, 1, 2,
2, 3, 0,
// Back
4, 5, 6,
4, 5, 7,
// Left
8, 9, 10,
10, 11, 8,
// Right
12, 13, 14,
14, 15, 12,
// Top
16, 17, 18,
18, 19, 16,
// Bottom
20, 21, 22,
22, 23, 20
};

最后,由于我们修改了顶点属性,所以我们要相应地修改vertex shader和glVertexAttribPointer的调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    glVertexAttribPointer(positionLocation,
3,
GL_FLOAT,
GL_FALSE,
sizeof(Vertex),
(GLvoid*)offsetof(Vertex,Position)); //下面是vertex shader attribute vec3 a_position; //注意之前我们使用的是vec2
attribute vec4 a_color; varying vec4 v_fragmentColor; void main()
{
gl_Position = CC_MVPMatrix * vec4(a_position.xyz,1); //这里用swizzle的时候是xyz
v_fragmentColor = a_color;
}

此时,编译运行,你会得到如下结果 :

cube01

别诧异,这就是一个立方体,只不过现在它离我们的“眼睛”(Cemera)很近,所以我们只能看到一个面。接下来,让我们修改一个modelView矩阵,让它离我们的camera远一点。

让立方体动起来

我们有很多方法可以让立方体转起来。比如直接修改modelView矩阵,也可以使用modelView配合projection矩阵。

首先,是最简单的方法,我们把整个立方体数据先缩小一半,然后再往-z轴方向移动0.5个单位,最后让它围绕着x轴不停地旋转。

1
2
3
4
5
6
7
8
9
10
11
modelViewMatrix.scale(0.5);
modelViewMatrix.translate(0.0,0, -0.5); static float rotation = 0;
modelViewMatrix.rotate(Vec3(1,0,0),CC_DEGREES_TO_RADIANS(rotation));
rotation++;
if (rotation < 360) {
rotation = 0;
} Director::getInstance()->multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, modelViewMatrix);

注意,这里我们操纵顶点的取值范围只能是-1~+1,xyz每一个轴都是这样。超出这个区域(normalized device space)就会裁剪掉。但是我们实际操作一个物体的移动的时,肯定不可能局限于这么小的范围,我们可以通过modelView和projection矩阵来定义一个更好用的坐标系,然后基于这个坐标系去指定物体的坐标。
比如cocos2d-x里面,通过下列代码指定了自己的坐标系范围在(0~size.width)和(0~size.height)之间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
case Projection::_3D:
{
float zeye = this->getZEye();
Mat4 matrixPerspective, matrixLookup;
loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
// issue #1334
Mat4::createPerspective(60, (GLfloat)size.width/size.height, 10, zeye+size.height/2, &matrixPerspective);
multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, matrixPerspective);
Vec3 eye(size.width/2, size.height/2, zeye), center(size.width/2, size.height/2, 0.0f), up(0.0f, 1.0f, 0.0f);
Mat4::createLookAt(eye, center, up, &matrixLookup);
multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, matrixLookup);
loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
break;
}

这里面,我们可以直接拿来用,也可以自己再写一个。下面是我用的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Mat4 projectionMatrix;
Mat4::createPerspective(60, 480/320, 1.0, 42, &projectionMatrix);
Director::getInstance()->multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, projectionMatrix); Mat4 modelViewMatrix;
Mat4::createLookAt(Vec3(0,0,1), Vec3(0,0,0), Vec3(0,1,0), &modelViewMatrix);
modelViewMatrix.translate(0, 0, -5); static float rotation = 0;
modelViewMatrix.rotate(Vec3(1,1,1),CC_DEGREES_TO_RADIANS(rotation));
rotation++;
if (rotation < 360) {
rotation = 0;
}
Director::getInstance()->multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, modelViewMatrix);

这里我让camera的位置位于(0,0,1),然后看着(0,0,0)点,并且头朝上(0,1,0)。大家可以尝试去修改createLookAt的参数,看看每一个参数具体是什么意思。这里有一个非常不错的程序介绍View Frustum的,强烈推荐!

最终效果:(如果你看不到,请升级你的浏览器!!!)

结语

附上本教程源码,从下篇文章开始,我们将介绍纹理映射。

推荐阅读

(转载)Cocos2dx-OpenGL ES2.0教程:你的第一个立方体(5)的更多相关文章

  1. Cocos2d-x中使用OpenGL ES2.0编写shader

    这几天在看子龙山人的关于OpenGL的文章,先依葫芦画瓢,能看到些东西,才能慢慢深入了解,当入门文章不错,但是其中遇到的一些问题,折腾了一些时间,为了方便和我一样的小白们,在这篇文章中进行写补充. O ...

  2. OpenGL ES2.0 入门经典例子

    原文链接地址:http://www.raywenderlich.com/3664/opengl-es-2-0-for-iphone-tutorial 免责申明(必读!):本博客提供的所有教程的翻译原稿 ...

  3. iOS开发——图形编程OC篇&OpenGL ES2.0编程步骤

    OpenGL ES2.0编程步骤 OpenGL ES (OpenGL for Embedded Systems) 是 OpenGL 三维图形 API 的子集,针对手机.PDA和游戏主机等嵌入式设备而设 ...

  4. Eclipse中通过Android模拟器调用OpenGL ES2.0函数操作步骤

    原文地址: Eclipse中通过Android模拟器调用OpenGL ES2.0函数操作步骤 - 网络资源是无限的 - 博客频道 - CSDN.NET http://blog.csdn.net/fen ...

  5. OpenGL ES2.0入门详解

    引自:http://blog.csdn.net/wangyuchun_799/article/details/7736928  1.决定你要支持的OpenGL ES的版本.目前,OpenGL ES包含 ...

  6. OPENGL ES2.0如何不使用glActiveTexture而显示多个图片

    https://www.oschina.net/question/253717_72107 用opengl es 2.0显示多个图片的话,我只会一种方式,先将图片生成纹理,然后用下面的方式渲染 // ...

  7. Android +NDK+eclipse+opengl ES2.0 开启深度測试

    參考:https://www.opengl.org/discussion_boards/showthread.php/172736-OpenGL-ES-Depth-Buffer-Problem 环境: ...

  8. OpenGL ES2.0 基本编程

    1. EGL OpenGL ES命令须要一个rendering context和一个drawing surface. Rendering Context: 保存当前的OpenGL ES状态. Draw ...

  9. (转载)Cocos2dx-OpenGL ES2.0教程:使用VBO索引(4)

    在上一篇文章中,我们介绍了uniform和模型-视图-投影变换,相信大家对于OpenGL ES 2.0应该有一点感觉了.在这篇文章中,我们不再画三角形了,改为画四边形.下篇教程,我们就可以画立方体了, ...

  10. (转载)Cocos2dx-OpenGL ES 2.0教程:你的第一个三角形(1)

    前言 在本系列教程中,我会以当下最流行的2D引擎Cocos2D-X为基础,介绍OpenGL ES 2.0的一些基本用法.本系列教程的宗旨是OpenGL扫盲,让大家在使用Cocos2D-X过程中,知其然 ...

随机推荐

  1. linux nginx启动 重启 关闭命令

    启动操作 nginx -c /usr/local/nginx/conf/nginx.conf -c参数指定了要加载的nginx配置文件路径 停止操作停止操作是通过向nginx进程发送信号来进行的 步骤 ...

  2. Android--仿QQ侧滑菜单

    首先还是先看一下效果图吧,这个示例在网上能找到很多,我主要在此是做一个小结和一些代码的分析,当是自己在学习过程中的一个积累,因为网上同样的文章很多,所以也无法探究最初的来源是哪里了. 我们可以从效果图 ...

  3. HTTP 错误 500.21 - Internal Server Error 处理程序“PageHandlerFactory-Integr

    将网站发布到IIS,访问发生如下错误: HTTP 错误 500.21 - Internal Server Error处理程序"PageHandlerFactory-Integr"在 ...

  4. SQL中PERSISTED关键字

    PERSISTED 指定 SQL Server 数据库引擎将在表中物理存储计算值,而且,当计算列依赖的任何其他列发生更新时对这些计算值进行更新.将计算列标记为 PERSISTED,可允许您对具有确定性 ...

  5. Cocos2d-x中__Dictionary容器以及实例介绍

    __Dictionary类在Cocos2d-x 2.x时代它就是CCDictionary类,它是模仿Objective-C中的NSDictionary类而设计的,通过引用计数管理内存.__Dictio ...

  6. Cocos2d-x中Vector<T>容器以及实例介绍

    Vector<T> 是Cocos2d-x 3.x推出的列表容器,因此它所能容纳的是Ref及子类所创建的对象指针,其中的T是模板,表示能够放入到容器中的类型,在Cocos2d-x 3.x中T ...

  7. PHP学习笔记 - 进阶篇(11)

    PHP学习笔记 - 进阶篇(11) 数据库操作 PHP支持哪些数据库 PHP通过安装相应的扩展来实现数据库操作,现代应用程序的设计离不开数据库的应用,当前主流的数据库有MsSQL,MySQL,Syba ...

  8. Koajs原理

    Koajs让习惯阻塞式代码写法的同学感到很舒服,再也不用盖楼式的callback了,而且也不需要学习Promise的then,catch这些新东西. 但实际上,Koajs这样的写法有点像是语言的语法糖 ...

  9. tinyMCE自定义添加图片插件

    需求: 在富文本编辑器中插入图片,图片来自用户可以自己上传的图片库. 本来可以用比较恶心的方式,也就是直接用tinyMCE自带的插入图片插件来实现.恶心是因为这个图片插件需要用户填入图片的url. 想 ...

  10. Java编程思想之字符串

    来自:Java编程思想(第四版) 第十三章 字符串   字符串操作是计算机程序中最常见的行为.   String对象是不可变的.查看JDK文档你就会发现,String类中每一个看起来会修改String ...