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

为什么三角形在OpenGL教程里面这么受欢迎呢?因为在OpenGL的世界里面,所有的几何体都可以用三角形组合出来。我们的四边形也一样,它可以用两个三角形组合出来。

你的第一个四边形

首先,因为OpenGL里面没有直接绘制四边形的命令的,所以我们需要画两个三角形来拼成一个四边形。这样的话,这样的话我们一共需要6个顶点。这6个顶点的坐标如下所示:

1
2
3
4
5
6
7
float vertercies[] =
{ -1,-1,
1, -1,
-1, 1,
-1, 1,
1,1,
1, -1};

此时,我们还需要修改glDrawArray和CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES宏,如下所示:

1
2
3
4
5
 //注意3个顶点,变成了6个顶点,这里一定要改成6,否则OpenGL只会画3个顶点
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
//这里的6是可选的,改成6可以更好地与cocos2d-x引擎融合
CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1, 6);

此时,运行程序,你会发现只有一个三角形。那是因为我们的顶点属性color只有3份,现在6个顶点了,所以也需要6份颜色数据。另外,为了显示好看,这里把6个颜色统一修改成绿色:

1
2
3
4
5
6
float color[] = { 0, 1,0, 1,
0,1,0, 1,
0, 1, 0, 1,
0, 1,0, 1,
0,1,0, 1,
0,1, 0, 1};

同时,记得把上一篇教程中设置的uniform u_color也重置一下:

1
2
float uColor[] = {1.0, 1.0, 1.0, 1.0};
glUniform4fv(uColorLocation,1, uColor);

此时,编译并运行,你会得到一个纯绿色的四边形:
greenrectangle

怎么样,画4边形不过如此吧,只需要多准备点数据就行啦。另外,注意一下三角形的顶点顺序。不过,细心的读者立马就会发现,我们的顶点数据里面有两个顶点是重复的。这其实是一种内存浪费。假设我们现在渲染一个复杂的模型,它可能包含几千个三角形,如果采用这种方式,那不知道要浪费多少内存。接下来,我们要介绍一种方法,使用索引数组来重用顶点数据。

使用VBO索引

推荐大家先看看VBO索引原理,相信大家看完之后应该知道怎么实现了。

  1. 修改原始顶点数据
    把vertercies修改为下面的样子:
1
2
3
4
5
float vertercies[] =
{ -1,-1,
1, -1,
-1, 1,
1,1};

从上面的顶点数据可以看出,这4个点刚好就是normalized device space的四个边界的顶点。

  1. 指定2个三角形的索引
    我们需要为两个三角形指定索引数据,如下所示:
1
2
GLubyte indices[] = { 0,1,2,  //第一个三角形索引
2,3,1}; //第二个三角形索引
  1. 创建索引缓冲区并绑定索引数据到缓冲区
1
2
3
4
GLuint indexVBO;
glGenBuffers(1, &indexVBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices) , indices, GL_STATIC_DRAW);

这里索引缓冲区与之前的GL_ARRAY_BUFFER的创建与使用方式是一样的。

  1. 最后,我们把glDrawArray替换成glDrawElements
1
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE,(GLvoid*)0);
  • 第一个参数:指定绘制基本图元的类型,这里我们指定的是三角形

  • 第二个参数:需要绘制的元素个数,即索引的数量,我们一共是6个索引

  • 第三个参数:指定索引数据的类型,注意必须是 GL_UNSIGNED_BYTE和GL_UNSIGNED_SHORT中的一个。推荐用GL_UNSIGNED_BYTE。

  • 第四4个参数:指定开始绘制的第一个索引数据在缓冲区的偏移。

此时,编译并运行,我们还是得到了和之前一样的四边形。

改进顶点数据结构

现在顶点属性包含位置(position)和颜色(color),将来还会有法线(normal),纹理坐标(texture coordinate)等数据。如果每一种类型的顶点数据都分开来存放,一来不利于管理,二来也会产生内存碎片。

在本小节中,我将向大家介绍如何使用一个结构体来封装这些数据。这也是cocos2d-x里面所用的方法,比如一个Quard的定义如下:

1
2
3
4
5
6
7
8
9
struct V3F_C4B_T2F
{
//! vertices (3F)
Vec3 vertices; // 12 bytes
//! colors (4B)
Color4B colors; // 4 bytes
// tex coords (2F)
Tex2F texCoords; // 8 bytes
};

仿照上面的结构体,我们也定义一个结构体Vertex,用来表示顶点的数据,目前它里面包含位置和颜色:

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

下面是我们的顶点数据定义:

1
2
3
4
5
6
7
Vertex data[] =
{
{ {-1,-1},{0,1,0,1} },
{ {1,-1},{0,1,0,1} },
{ {-1,1},{0,1,0,1} },
{ {1,1},{0,1,0,1} }
};

注意,我们画四边形需要4个顶点,所以,需要四份Vertex数据。接下来,我们指定Vertex Shader如何读取这些属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 glVertexAttribPointer(positionLocation,
2,
GL_FLOAT,
GL_FALSE,
sizeof(Vertex),
(GLvoid* )offsetof(Vertex,Position));
//set for color
// glGenBuffers(1, &colorVBO);
// glBindBuffer(GL_ARRAY_BUFFER, colorVBO);
// glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
GLuint colorLocation = glGetAttribLocation(program->getProgram(), "a_color");
glEnableVertexAttribArray(colorLocation);
glVertexAttribPointer(colorLocation,
4,
GL_FLOAT,
GL_FALSE,
sizeof(Vertex),
(GLvoid* )offsetof(Vertex,Color));

这里,我们需要指定glVertexAttribPointer的第5个参数和第6个参数。

下图告诉我们Vertex Shader是如何读取属性的:
multiplevertexattri

注意,我们这里把colorVBO的生成和绑定代码注释掉了,因为已经不需要了。
编译并运行,这时候你还是会看到一个绿色的四边形。

结语

从本例中可以看到,VBO可以一次性传递所有的顶点数据给vertex shader(目前是position和color,以后还有法线和纹理坐标),然后使用glVertexAttribPointer按一定的规则去取数据就行了。至于几何图元如何组装,可以交给索引VBO去解决,最后调用glDrawElements来完成实际的绘制。

另外如果我们只想画纯色的四边形,那么建议不要使用attribute,直接使用uniform并把该uniform的值传给gl_FragColor就行了。这个就留给读者自行去实验啦。

本教程源码下载地址

推荐阅读

(转载)Cocos2dx-OpenGL ES2.0教程:使用VBO索引(4)的更多相关文章

  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教程:你的第一个立方体(5)

    在上篇文章中,我们介绍了VBO索引的使用,使用VBO索引可以有效地减少顶点个数,优化内存,提高程序效率. 本教程将带领大家一起走进3D–绘制一个立方体.其实画立方体本质上和画三角形没什么区别,所有的模 ...

随机推荐

  1. jfreechart的使用案例

    一:下载需要的2个jar文件 jcommon-版本号.jar,jfreechart-版本号.jar,注意版本不要太高. 实例一:比较简单的application版本的饼图 /** * */ packa ...

  2. jQuery选择器解释和说明

    jQuery选择器的意义在于快速的找出特定的DOM元素,然后添加相应的行为. 基本选择器 //选择 id为 one 的元素 $('#btn1').click(function(){ $('#one') ...

  3. Exception与相关

    怎么写一个exception类, 直接抛出去,主要是写一个构造函数里面的Msg消息,这个可以提前写出来. try...catch..finally 一般都是一起的,try 中有异常执行语句, catc ...

  4. 安装程序无法初始化。请下载Adobe Support Advisor检测该问题

    adobe FLASH BUILDER 4.6在安装时出现各种各样的问题,虽然绿化版安装方便,但是平均5分钟一崩溃实在让人头大.安装时出现“安装程序无法初始化.请下载Adobe Support Adv ...

  5. request对象实现请求转发

    request对象实现请求转发,请求转发指一个web资源收到客户端请求后,通知服务器去调用另外一个web资源进行处理.request对象提供了一个getRequestDispatcher方法,该方法返 ...

  6. Agile.Net 组件式开发平台 - 组件开发示例

    所谓组件式开发平台,它所有的功能模块都是以组件的形式扩展的,下面我来演示一个简单的组件开发例程. Agile.Net开发管理平台项目,已经托管在开源中国码云平台(http://git.oschina. ...

  7. Agile.Net 组件式开发平台 - 权限管理组件

    RBAC原则       (1)最小权限原则之所以被RBAC所支持,是因为RBAC可以将其角色配置成其完成任务所需要的最小的权限集.       (2)责任分离原则可以通过调用相互独立互斥的角色来共同 ...

  8. Windows7 下配置添加ASP功能

    按照如下顺序添加 1.控制面板-程序-打开或关闭Windows功能 2.Internet信息服务-万维网服务-应用程序开发功能 3.勾选ASP 和ASP.net选项 确定后安装完毕即可支持.

  9. Cocoa 框架为什么采用两阶段的方式来创建对象?

    对于之前一直使用C#语言的我来说,刚开始接触Objective-c来创建对象时很迷惑,为何创建对象一般情况下需要通过发送两个消息(调用两个方法)才能创建一个类实例对象(例如[[UIButton all ...

  10. IOS UITabelView的cell

    一.Cell的重用原理 iOS设备的内存有限,如果用UITableView显示成千上万条数据,就需要成千上万个UITableViewCell对象的话,那将会耗尽iOS设备的内存.要解决该问题,需要重用 ...