上一篇文章介绍了cocos2d-x的基本渲染结构,这篇顺着之前的渲染结构介绍渲染命令QUAD_COMMAND命令的部分,通过这部分的函数,学习opengl处理图片渲染的方法,首先介绍这节需要涉及到的基本概念VAO和VBO。
VAO和VBO:
顶点数组对象(Vertex Array Object 即VAO)是一个包含一个或数个顶点缓冲区对象(Vertex Buffer Object, 即 VBO)的对象,一般存储一个可渲染物体的所有信息。顶点缓冲区对象(VertexBuffer Object VBO)是你显卡内存中的一块高速内存缓冲区,用来存储顶点的所有信息。
这些概念显得很晦涩,简而言之,一般我们绘制一些图形需要将所有顶点的信息存储在一个数组里,但是经常会出现一些点是被重复使用的,这样就会出现一个点的信息的存储空间被重复使用的问题,这样第一会造成存储控件的浪费,第二就是如果我们要修改这个点的信息,需要改多次。所以我们采用索引的方式来描述图形,这样可以用一个数组存储点的信息,另外一个数组存储点的索引,这样所有的点都是不同的,另外把顶点信息存储在显卡的内存中,减少了cpu向gpu传输数据的时间,提高了程序的渲染效率,这就是VBO,在OpenGL3.0中,出现了更进一步的VAO,VBO通过绘制上下文获得绘制状态,VAO可以拥有多个VBO,它记录所有绘制状态,它的代码更简洁,效率更高,在cocos2d-x的绘制中,我们会判断底层是否支持VAO,如果支持VAO,那么优先采用VAO绘制。二者的区别可以从初始化就可以看出来:
- voidRenderer::setupBuffer()
- {
- if(Configuration::getInstance()->supportsShareableVAO())
-
- setupVBOAndVAO();
- }
- else
- {
-
- setupVBO();
- }
- voidRenderer::setupVBOAndVAO()
- //一个VAO
- glGenVertexArrays(1,&_quadVAO);
- //绑定VAO
- GL::bindVAO(_quadVAO);
- //创建生成两个VBO
- glGenBuffers(2,&_buffersVBO[0]);
- //顶点Buffer
- glBindBuffer(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,153); background-color:inherit; font-weight:bold">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,colors));
- //纹理坐标数据
- glEnableVertexAttribArray(GLProgram::VERTEX_ATTRIB_TEX_COORDS);
- glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORDS,2,texCoords));
- //索引Buffer
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,_buffersVBO[1]);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER,153); background-color:inherit; font-weight:bold">sizeof(_indices[0])*VBO_SIZE*6,_indices,GL_STATIC_DRAW);
- //取消VAO
- GL::bindVAO(0);
-
- CHECK_GL_ERROR_DEBUG();
- voidRenderer::setupVBO()
- //创建生成两个VBO
- glGenBuffers(2,&_buffersVBO[0]);
- //调用函数绑定buffer
- mapBuffers();
- voidRenderer::mapBuffers()
- //GL_ARRAY_BUFFER表示顶点数据
- //GL_ELEMENT_ARRAY_BUFFER表示索引数据
- //避免改变buffer元素
- //绑定id顶点数据
- //为改id制定一段内存区域
- glBufferData(GL_ARRAY_BUFFER,GL_DYNAMIC_DRAW);
- glBindBuffer(GL_ARRAY_BUFFER,0); background-color:inherit">//第二个VBO索引数据
-
- CHECK_GL_ERROR_DEBUG();
- }
需要介绍的两个关键的函数
glBindBuffer:它绑定缓冲区对象表示选择未来的操作将影响哪个缓冲区对象。如果应用程序有多个缓冲区对象,就需要多次调用glBindBuffer()函数:一次用于初始化缓冲区对象以及它的数据,以后的调用要么选择用于渲染的缓冲区对象,要么对缓冲区对象的数据进行更新。
当传入的第二个参数第一次使用一个非零无符号整数时,创建一个新的缓冲区对象;当第二个参数是之前使用过的,这个缓冲区对象成为活动缓冲区对象;如果第二个参数值为0时,停止使用缓冲区对象
glBufferData:保留空间存储数据,他分配一定大小的(第二个参数)的openGL服务端内存,用于存储顶点数据或索引。这个被绑定的对象之前相关联的数据都会被清除。
glBufferData参数介绍
参数1,目标GL_ARRAY_BUFFER或者GL_ELEMENT_ARRAY_BUFFER
参数2,内存容量
参数3,用于初始化缓冲区对象,可以使一个指针,也可以是空
参数4,如何读写,可以选择如下几种
GL_DYNAMIC_DRAW:多次指定,多次作为绘图和图像指定函数的源数据,缓冲区对象的数据不仅常常需要进行更新,而且使用频率也非常高
GL_STATIC_DRAW:数据只指定一次,多次作为绘图和图像指定函数的源数据,缓冲区对象的数据只指定1次,但是这些数据被使用的频率很高
GL_STREAM_DRAW:数据只指定一次,最多只有几次作为绘图和图像指定函数的源数据,缓冲区对象中的数据常常需要更新,但是在绘图或其他操作中使用这些数据的次数较少
从初始化的代码上,为什么VAO反倒复杂了呢?因为他只是把绘制时需要做的一些事情提前放到初始化函数中,来看一下绘制流程。