从Cocos2d-x学习OpenGL--解析QUAD_COMMAND

VAO和VBO

顶 点数组对象(Vertex Array Object即VAO)是一个包含一个或数个顶点缓冲区对象(Vertex Buffer Object, 即VBO)的对象,一般存储一个可渲染物体的所有信息。顶点缓冲区对象(VertexBuffer ObjectVBO)是你显卡内存中的一块高速内存缓冲区,用来存储顶点的所有信息。

这些概念显得很晦涩,简而言之,一般我们绘制一些图形需要将所有顶点的信息存储在一个数组里,但是经常会出现一些点是被重复使用的,这样就会出现一个点的信息的存储空间被重复使用的问题,这样第一会造成存储控件的浪费,第二就是如果我们要修改这个点的信息,需要改多次。所以我们采用索引的方式来描述图形,这样可以用一个数组存储点的信息,另外一个数组存储点的索引,这样所有的点都是不同的,另外把顶点信息存储在显卡的内存中,减少了cpu向gpu传输数据的时间,提高了程序的渲染效率,这就是VBO,在OpenGL3.0中,出现了更进一步的VAO,VBO通过绘制上下文获得绘制状态,VAO可以拥有多个VBO,它记录所有绘制状态,它的代码更简洁,效率更高,在Cocos2d-x的绘制中,我们会判断底层是否支持VAO ,如果支持VAO,那么优先采用VAO绘制。二者的区别可以从初始化就可以看出来:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869voidRenderer::setupBuffer(){if(Configuration::getInstance()->supportsShareableVAO()){//初始化VBO和VAOsetupVBOAndVAO();}else{//不支持VAO,只初始化VBOsetupVBO();}}voidRenderer::setupVBOAndVAO(){//一个VAOglGenVertexArrays(1,&_quadVAO);//绑定VAOGL::bindVAO(_quadVAO);//创建生成两个VBOglGenBuffers(2,&_buffersVBO[0]);//顶点BufferglBindBuffer(GL_ARRAY_BUFFER,_buffersVBO[0]);glBufferData(GL_ARRAY_BUFFER,sizeof(_quads[0])*VBO_SIZE,_quads,GL_DYNAMIC_DRAW);//这里就是VAO和VBO的区别,VAO把这些放到初始化中,无论后面绘制多少次,只要他不被改变,这段代码只会被调用一次,而VBO中,这个功能的代码会在每次被绘制时调用,这样就节约了效率//位置glEnableVertexAttribArray(GLProgram::VERTEX_ATTRIB_POSITION);glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION,3,GL_FLOAT,GL_FALSE,sizeof(V3F_C4B_T2F),(GLvoid*)offsetof(V3F_C4B_T2F,vertices));//颜色glEnableVertexAttribArray(GLProgram::VERTEX_ATTRIB_COLOR);glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR,4,GL_UNSIGNED_BYTE,GL_TRUE,sizeof(V3F_C4B_T2F),(GLvoid*)offsetof(V3F_C4B_T2F,colors));//纹理坐标数据glEnableVertexAttribArray(GLProgram::VERTEX_ATTRIB_TEX_COORDS);glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORDS,2,GL_FLOAT,GL_FALSE,sizeof(V3F_C4B_T2F),(GLvoid*)offsetof(V3F_C4B_T2F,texCoords));//索引BufferglBindBuffer(GL_ELEMENT_ARRAY_BUFFER,_buffersVBO[1]);glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(_indices[0])*VBO_SIZE*6,_indices,GL_STATIC_DRAW);//取消VAOGL::bindVAO(0);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);glBindBuffer(GL_ARRAY_BUFFER,0);CHECK_GL_ERROR_DEBUG();}voidRenderer::setupVBO(){//创建生成两个VBOglGenBuffers(2,&_buffersVBO[0]);//调用函数绑定buffermapBuffers();}voidRenderer::mapBuffers(){//GL_ARRAY_BUFFER表示顶点数据//GL_ELEMENT_ARRAY_BUFFER表示索引数据//避免改变buffer元素GL::bindVAO(0);//绑定id顶点数据glBindBuffer(GL_ARRAY_BUFFER,_buffersVBO[0]);//为改id制定一段内存区域glBufferData(GL_ARRAY_BUFFER,sizeof(_quads[0])*VBO_SIZE,_quads,GL_DYNAMIC_DRAW);glBindBuffer(GL_ARRAY_BUFFER,0);//第二个VBO索引数据glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,_buffersVBO[1]);glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(_indices[0])*VBO_SIZE*6,_indices,GL_STATIC_DRAW);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);CHECK_GL_ERROR_DEBUG();}

需要介绍的两个关键的函数

当传入的第二个参数第一次使用一个非零无符号整数时,创建一个新的缓冲区对象;当第二个参数是之前使用过的,这个缓冲区对象成为活动缓冲区对象;如果第二个参数值为0时,停止使用缓冲区对象。

glBufferData参数介绍

参数1,目标GL_ARRAY_BUFFER或者GL_ELEMENT_ARRAY_BUFFER

参数2,内存容量

参数3,用于初始化缓冲区对象,可以使一个指针,也可以是空

参数4,如何读写,可以选择如下几种:

从初始化的代码上,为什么VAO反倒复杂了呢?因为他只是把绘制时需要做的一些事情提前放到初始化函数中,来看一下绘制流程。

从Cocos2d-x学习OpenGL--解析QUAD_COMMAND
123456789101112131415161718192021222324252627282930313233//当前的openGL是否支持VAOif(Configuration::getInstance()->supportsShareableVAO()){//绑定顶点数组glBindBuffer(GL_ARRAY_BUFFER,_buffersVBO[0]);//向缓冲区申请空间并指定数据传输方式glBufferData(GL_ARRAY_BUFFER,sizeof(_quads[0])*(_numQuads),nullptr,GL_DYNAMIC_DRAW);//提供缓冲区对象包含整个数据集合的更新void*buf=glMapBuffer(GL_ARRAY_BUFFER,GL_WRITE_ONLY);memcpy(buf,_quads,sizeof(_quads[0])*(_numQuads));//缓冲区对象的更新完成glUnmapBuffer(GL_ARRAY_BUFFER);//为了禁用缓冲区对象,可以用0作为缓冲区对象的标识符来调用glBindBuffer()函数。这将把OpenGL切换为默认的不使用缓冲区对象的模式。glBindBuffer(GL_ARRAY_BUFFER,0);//BindVAOGL::bindVAO(_quadVAO);}else{#definekQuadSizesizeof(_quads[0].bl)glBindBuffer(GL_ARRAY_BUFFER,_buffersVBO[0]);glBufferData(GL_ARRAY_BUFFER,sizeof(_quads[0])*_numQuads,_quads,GL_DYNAMIC_DRAW);//激活顶点颜色纹理坐标的属性GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POS_COLOR_TEX);//顶点glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION,3,GL_FLOAT,GL_FALSE,kQuadSize,(GLvoid*)offsetof(V3F_C4B_T2F,vertices));//颜色glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR,4,GL_UNSIGNED_BYTE,GL_TRUE,kQuadSize,(GLvoid*)offsetof(V3F_C4B_T2F,colors));//纹理坐标glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORDS,2,GL_FLOAT,GL_FALSE,kQuadSize,(GLvoid*)offsetof(V3F_C4B_T2F,texCoords));glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,_buffersVBO[1]);}

可以看到,这些设置属性的函数放在了绘制函数里,虽然看似是一样的,但是绘制函数会被调用的更频繁,所以把这些函数放到初始化函数中可以大幅提高程序的效率。

这里介绍VAO的两个函数

最后需要调用绘制元素函数,绘制这些信息:

1glDrawElements(GL_TRIANGLES,(GLsizei)quadsToDraw*6,GL_UNSIGNED_SHORT,(GLvoid*)(startQuad*6*sizeof(_indices[0])));

它根据索引绘图(注意:顶点数据和索引各自使用不同的缓冲区)。

需要注意的是在Renderer的析构函数中要调用glDeleteBuffers来释放它的资源,并使它的标识可以其他缓冲区对象使用。

上一篇中介绍的几种渲染命令中的QUAD_COMMAND(这里把它称作四边形绘制)命令回调用drawBatchedQuads调用绘制函数,处理这个逻辑的命令是这样的:

12345678910111213141516171819if(commandType==RenderCommand::Type::QUAD_COMMAND){autocmd=static_cast(command);CCASSERT(nullptr!=cmd,"IllegalcommandforRenderCommandTagedasQUAD_COMMAND");//如果Quad数据量超过VBO的大小,那么调用绘制,将缓存的命令全部绘制if(_numQuads+cmd->getQuadCount()>VBO_SIZE){CCASSERT(cmd->getQuadCount()>=0&&cmd->getQuadCount()<VBO_SIZE,"VBOisnotbigenoughforquaddata,pleasebreakthequaddatadownorusecustomizedrendercommand");drawBatchedQuads();}//将命令缓存起来,先不调用绘制_batchedQuadCommands.push_back(cmd);memcpy(_quads+_numQuads,cmd->getQuads(),sizeof(V3F_C4B_T2F_Quad)*cmd->getQuadCount());//转换成世界坐标convertToWorldCoordinates(_quads+_numQuads,cmd->getQuadCount(),cmd->getModelView());//记录下四边形数量_numQuads+=cmd->getQuadCount();}
1234567voidRenderer::flush(){//绘制drawBatchedQuads();//清空_lastMaterialID=0;}

这个处理主要是把命令存入_batchedQuadCommands中,如果如果Quad数据量超过VBO的大小,那么调用绘制,将缓存的命令全部绘制。

如果一直没有超过VBO的大小,drawBatchedQuads绘制函数将在flush被调用时调用。

如有错误,欢迎指出。下一篇介绍图形渲染和批处理。

来源网址:http://blog.csdn.net/bill_man/article/details/38314077

  

爱华网本文地址 » http://www.413yy.cn/a/25101014/225900.html

更多阅读

cocos2d-x图片纹理优化资源加载方案 cocos2dx 资源优化

百度乱搜了一阵,居然搜着一篇神贴。真有大神愿意花很多时间去把自己得到的经验写出来,心里实在佩服。而且这篇文章写的通俗易懂。文章主要解决了我一直以来疑惑的几个问题1.到底用不用2的N次幂的图片2.为什么加载资源的时候,内存会突然

从零开始学习电脑硬件知识菜鸟扫盲

很多使用电脑的朋友, 没有学习电脑硬件知识, 很多常识生的问题也解决不了,经常感到 电脑知识普及的必要性,有很多基本的知识可以有助于更好地学习电脑的应用。这里简单介绍电脑硬件的组成 先让想学习电脑硬件而又对硬件不熟悉的朋友

wifidog-从零开始学习OpenWrt完美教程

现在有越来越多的Maker开始折腾OpenWrt,但作为一个Maker新手来讲,在网上还是很难找到一份系统的入门级资料。查找资料很辛苦,而且OpenWrt的门槛相对较高,希望这篇文章所提供的从零开始学OpenWrt编译+刷机+使用教程能降低新手们的入门难

人格决定绩效(7)—从冲突中学习

     在日常工作或生活中,我们每个人不时会对某个人感到不耐烦、不满,甚至是两个人之间会很容易产生冲突。我就曾经和一位部门主管产生这种情况。这位部门主管是表现出讲义气的,并且为了员工而出现了偏离原则的情况。我对此非常恼

声明:《从Cocos2d-x学习OpenGL--解析QUAD_COMMAND》为网友莪卜怕輸分享!如侵犯到您的合法权益请联系我们删除