基于Cocos2d-x学习OpenGL ES 2.0系列——使用VBO索引(4)

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

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

你的第一个四边形

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

float vertercies[] =
        { -1,-1,
        1, -1,
        -1, 1,
        -1, 1,
        1,1,
        1, -1};

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

//注意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个颜色统一修改成绿色:

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也重置一下:

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

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

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

使用VBO索引

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

1)修改原始顶点数据

把vertercies修改为下面的样子:

float vertercies[] =
        { -1,-1,
        1, -1,
        -1, 1,
        1,1};

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

2)指定2个三角形的索引

我们需要为两个三角形指定索引数据,如下所示:

GLubyte indices[] = { 0,1,2,  //第一个三角形索引
                    2,3,1}; //第二个三角形索引

3)创建索引缓冲区并绑定索引数据到缓冲区

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的创建与使用方式是一样的。

4)最后,我们把glDrawArray替换成glDrawElements

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的定义如下:

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

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

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

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

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如何读取这些属性:

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是如何读取属性的:

注意,我们这里把colorVBO的生成和绑定代码注释掉了,因为已经不需要了。

编译并运行,这时候你还是会看到一个绿色的四边形。

结语

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

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

点击下载本教程源码。

推荐阅读:

顶点属性

使用VBO索引

来源网址:http://4gamers.cn/archives/331

时间: 05-05

基于Cocos2d-x学习OpenGL ES 2.0系列——使用VBO索引(4)的相关文章

基于Cocos2d-x学习OpenGL ES 2.0系列——你的第一个三角形(1)

[本系列转自]http://cn.cocos2d-x.org/tutorial/lists?id=79 前言 在本系列教程中,我会以当下最流行的2D引擎Cocos2d-x为基础,介绍OpenGL ES 2.0的一些基本用法.本系列教程的宗旨是OpenGL扫盲,让大家在使用Cocos2d-x过程中,知其然,更知其所以然.本系列教程不会涉及非常底层的数学原理,同时也不会过多地提及OpenGL本身的一些细节知识.但是我会在每篇文章的最后给出一些参考链接,大家可以顺藤摸瓜,一举Get OpenGL这个新

基于Cocos2d-x学习OpenGL ES 2.0系列——OpenGL ES渲染之Shader准备(7)

Cocos2d-x底层图形绘制是使用OpenGL ES协议的.OpenGL ES是什么呢? OpenGL ES(OpenGl for Embedded System)是OpenGL三维图形API的子集,针对手机.Pad和游戏主机等嵌入式设备而设计.该API由Khronos集团定义推广,Khronos是一个图形软硬件行业协会,该协会主要关注图形和多媒体方面的开放标准.OpenGL ES是OpenGL三维图形API的子集,针对手机.Pad和游戏主机等嵌入式设备而设计.Cocos2d-x底层图形渲染使

基于Cocos2d-x学习OpenGL ES 2.0系列——你的第一个立方体(5)

在上篇文章中,我们介绍了VBO索引的使用,使用VBO索引可以有效地减少顶点个数,优化内存,提高程序效率. 本教程将带领大家一起走进3D--绘制一个立方体.其实画立方体本质上和画三角形没什么区别,所有的模型最终都要转换为三角形. 同时,本文还会介绍如何通过修改MVP矩阵来让此立方体不停地旋转.另外,大家还可以动手去修改本教程的示例代码,借此我们可以更加深入地理解OpenGL的normalized device space. 准备立方体数据 在开始真正的绘制代码之前,我们先要准备好数据.首先,我们需

基于Cocos2d-x学习OpenGL ES 2.0系列——OpenGL ES渲染之LayerColor(8)

在前面文章中讲述了Cocos2d-x引擎OpenGL渲染准备Shader方面,本文主要讲解使用LayerColor来讲述OpenGL的渲染过程. 1.LayerColor对象创建 添加LayerColor元素到游戏中: autolayerColor = LayerColor::create(Color4B(255, 0, 0, 255), 100, 100); layerColor->setPosition(100,100); 下面是LayerColor::create方法: LayerColo

基于Cocos2d-x学习OpenGL ES 2.0系列——编写自己的shader(2)

在上篇文章中,我给大家介绍了如何在Cocos2d-x里面绘制一个三角形,当时我们使用的是Cocos2d-x引擎自带的shader和一些辅助函数.在本文中,我将演示一下如何编写自己的shader,同时,我们还会介绍VBO(顶点缓冲区对象)和VAO(顶点数组对象)的基本用法. 在编写自己的shader之前,我觉得有必要提一下OpenGL渲染管线. 理解OpenGL渲染管线,对于学习OpenGL非常重要.下面是OpenGL渲染管线的示意图:(图中淡蓝色区域是可以编程的阶段) 此图是从wiki中拿过来的

基于Cocos2d-x学习OpenGL ES 2.0系列——初识MVP(3)

在上一篇文章中,我在介绍vertex shader的时候挖了一个坑:CC_MVPMatrix.它其实是一个uniform,每一个Cocos2d-x预定义的shader都包含有这个uniform,但是如果你在shader里面不使用这个变量的话,OpenGL底层会把它优化掉. 但是,CC_MVPMatrix是在什么时候设置进来的呢?我在shader里面明明没有看到它,它从哪儿来的?别急,请继续往下读. 初识Uniform 在回答上面几个问题之前,让我们先来介绍一下什么是uniform.简单来说,un

基于Cocos2d-x学习OpenGL ES 2.0系列——纹理贴图(6)

在上一篇文章中,我们介绍了如何绘制一个立方体,里面涉及的知识点有VBO(Vertex Buffer Object).IBO(Index Buffer Object)和MVP(Modile-View-Projection)变换. 本文将在教程4的基础之上,添加纹理贴图支持.最后,本文会把纹理贴图扩展至3D立方体上面. 基本方法 当我们把一张图片加载到内存里面之后,它是不能直接被GPU绘制出来的,纹理贴图过程如下: 首先,我们为之前的顶点添加纹理坐标属性并传到vertex shader里面去: 然后

Hello Triangle:OpenGL ES 2.0 版的“Hello world”

Hello Triangle:OpenGL ES 2.0 版的"Hello world" 本文的文字大部分都是从<OpenGL ES 2.0 编程向导>中摘抄而来,特此说明. 该文是基于OpengGL ES 2.0的,算是本人学习OpenGL的"Hello world"吧. 一个OpengGL ES 2.0程序的实现大致如下所示: 装载顶点和片段着色器. 创建一个项目对象,联系顶点和片段着色器,链接项目. 设置视窗. 清除颜色缓冲区. 最基本的渲染. 准

Chapter 1 : OpenGLES 3.0 简介 (2)—— OpenGL ES 3.0

管道 如前所属,本书讲解的API版本是OpenGL ES 3.0.本书的目标是,深入讲解OpenGL ES 3.0的技术细节,给出具体的例子来说明如何使用某个特性,并且讨论了各种性能优化技术.当您读完这本书,您应该可以对OpenGL ES 3.0API有一个很好的把握.您将可以轻松的写出让人新服的OpenGL ES 3.0的应用程序,并且您不必通过阅读多种OpenGL ES的规范来搞懂某个特性是如何工作的. OpenGL ES 3.0实现了可编程着色图形管道.OpenGL ES 3.0规范包含两