<html>

        个人原创,欢迎转载,转载请注明原文地址http://blog.csdn.net/bill_man

        上一篇文章介绍了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绘制。二者的差别能够从初始化就能够看出来:

void Renderer::setupBuffer()
{
    if(Configuration::getInstance()->supportsShareableVAO())
    {
        //初始化VBO和VAO
        setupVBOAndVAO();
    }
    else
    {
        //不支持VAO,仅仅初始化VBO
        setupVBO();
    }
}
void Renderer::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, 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));
    //索引Buffer
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(_indices[0]) * VBO_SIZE * 6, _indices, GL_STATIC_DRAW);
    //取消VAO
    GL::bindVAO(0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    CHECK_GL_ERROR_DEBUG();
}
void Renderer::setupVBO()
{
    //创建生成两个VBO
    glGenBuffers(2, &_buffersVBO[0]);
    //调用函数绑定buffer
    mapBuffers();
}
void Renderer::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();
}

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

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反倒复杂了呢?由于他仅仅是把绘制时须要做的一些事情提前放到初始化函数中,来看一下绘制流程。

    //当前的openGL是否支持VAO
    if (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); //Bind VAO GL::bindVAO(_quadVAO); } else { #define kQuadSize sizeof(_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的两个函数:

glMapBuffer函数返回一个指针。指向与第一个參数相关联的当前绑定缓冲区对象的数据存储。第一个參数与glBufferData的第一个參数一致。第二个參数是GL_READ_ONLY、GL_WRITE_ONLY或GL_READ_WRITE之中的一个,表示能够对数据进行的操作。

glUnmapBuffer表示对当前绑定缓冲区对象的更新已经完毕,并且这个缓冲区能够释放。

enableVertexAttribs激活相关属性,激活的属性能够调用glVertexAttribPointer指定数据源。可选的有VERTEX_ATTRIB_FLAG_POSITION,VERTEX_ATTRIB_FLAG_COLOR和VERTEX_ATTRIB_FLAG_TEX_COORDS,这里这个參数是激活这三个。

glVertexAttribPointer指定了渲染时第一个參数代表的索引值的顶点属性数组的数据格式和位置。

第一个參数指定要改动的顶点属性的索引值,包括VERTEX_ATTRIB_POSITION(位置)。VERTEX_ATTRIB_COLOR(颜色),VERTEX_ATTRIB_TEX_COORDS(纹理坐标)。

第二个參数指定每一个属性值的组件数量且必须为1、2、3、4之中的一个。

第三个參数指定数组中每一个组件的数据类型。可用的符号常量有GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT,GL_UNSIGNED_SHORT,GL_FIXED, 和 GL_FLOAT。初始值为GL_FLOAT。

第四个參数指定当被訪问时,固定点数据值是否应该被归一化(GL_TRUE,意味着整数型的值会被映射至区间[-1,1](有符号整数)。或者区间[0,1](无符号整数))或者直接转换为固定点值(GL_FALSE)。

第五个參数指定了一个属性到下一个属性之间的步长(这就同意属性值被存储在单一数组或者不同的数组中)。也就是连续顶点属性之间的偏移量。假设为0,那么它们是紧密排列在一起的。初始值为0。

第六个參数指定一个指针,指向数组中第一个顶点属性的第一个组件。初始值为0。

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

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

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

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

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

if(commandType == RenderCommand::Type::QUAD_COMMAND)
{
    auto cmd = static_cast<QuadCommand*>(command);
    CCASSERT(nullptr!= cmd, "Illegal command for RenderCommand Taged as QUAD_COMMAND");
                    
    //假设Quad数据量超过VBO的大小。那么调用绘制。将缓存的命令全部绘制
    if(_numQuads + cmd->getQuadCount() > VBO_SIZE)
    {
        CCASSERT(cmd->getQuadCount()>= 0 && cmd->getQuadCount() < VBO_SIZE, "VBO is not big enough for quad data, please break the quad data down or use customized render command");
        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();
}

       

void Renderer::flush()
{
    //绘制
    drawBatchedQuads();
    //清空
    _lastMaterialID = 0;
}

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

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


如有错误,欢迎指出

下一篇介绍图形渲染和批处理


版权声明:本文为博主原创文章。未经博主同意不得转载。 举报

相关文章推荐

《游戏引擎架构》读书笔记-游戏引擎中的C++

《游戏引擎架构》在两年前就买了。并且自己也大体的看过一遍,由于自己想学习引擎的底层知识,所以又一次又找出了这本书再读一遍,并把依据自己的理解写一些读书笔记在这里与大家交流。       闲话少续,開始本...

cocos2D-X源代码分析之从cocos2D-X学习OpenGL(1)----cocos2D-X渲染结构

cocos2D-X 3.0渲染结构代码解说,也是cocos2D-X源代码解说系列文章和从cocos2D-X学习OpenGL系列文章的開始

cocos2D-X源代码分析之从cocos2D-X学习OpenGL(8)----纹理

纹理(Texture)就是图片,它用来给物体添加细节,cocos2d-x中使用Texture2D类处理2D纹理贴图,本篇就从cocos2d-x中的Texture2D类介绍openGL纹理。      ...

cocos2D-X源代码分析之从cocos2D-X学习OpenGL(4)---混合

之前在项目中就使用过混合,可是研究的不深入,最近美术的一个需求让我下决心又一次深入的研究了一下混合以及它在cocos2d-x中的使用。在这里分享给大家。

cocos2D-X源代码分析之从cocos2D-X学习OpenGL(18)----实例化

cocos2d-x中的batchNode的方式能够提高同样的纹理的渲染效率,可是它使用的绘制方式还是glDrawElements的方式。它仅仅是把同样的纹理的绘制命令仅仅提交一次,剩下的反复绘制都调用gl...

cocos2D-X源代码分析之从cocos2D-X学习OpenGL(7)----GLSL

上一篇博客介绍了cocos2d-x中的着色器类相关的结构,以及着色器的一些原理。这一篇将介绍着色器语言。

cocos2D-X源代码分析之从cocos2D-X学习OpenGL(5)---绘制基本图形

近期完整的学习了learnopengl(http://www.learnopengl.com/),认为很有启示,从而又想起了这个长草许久的专题。正好趁这段时间,从本篇起完毕这个专题,须要说明的是,从...

cocos2D-X源代码分析之从cocos2D-X学习OpenGL(19)----旋转表示法

从数学上表示旋转,能够有三种表示方法:矩阵法。欧拉角法和四元数法,我们之前已经介绍了矩阵法。它有个问题。就是easy造成万向节死锁。所谓万向节死锁,就是当绕一个轴旋转到90度的时候,再绕另外两个轴旋转的结...

cocos2d-x性能优化的那些事

年前在对我做的项目做性能优化,尽管在开发中。性能问题是一直关注着的,可是这个东西依旧须要在后期做一段时间的优化的。也遇到不少坑。在这里分享下,也记作笔记,另外也欢迎大家有这方面的问题经验在这里讨论。

性...

cocos2D-X源代码分析之从cocos2D-X学习OpenGL(6)---cocos内置着色器

上一篇我们介绍了cocos2d-x绘制基本图形的基本流程。我们还留下了一个着色器的部分没有讲,本篇内容将从openGL的渲染流程讲起。介绍cocos2d-x中的着色器。openGL的渲染流程如图所看到的:...
  • 微博
    微信
    QQ
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多仅仅同意输入30个字)

posted @ 2017-08-18 14:47  cxchanpin  阅读(191)  评论(0编辑  收藏  举报