zoukankan      html  css  js  c++  java
  • cocos2D-X从的源代码的分析cocos2D-X学习OpenGL(1)----cocos2D-X渲染架构

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

            从本篇文章開始,将分析cocos2D-X 3.0源码,第一部分是从cocos2D-X学习OpenGL,也就是分析cocos2D-X 3.0的渲染代码,本篇首先介绍cocos2D-X 3.0的渲染结构。使用的是3.0正式版。

    void DisplayLinkDirector::mainLoop()
    {
        if (_purgeDirectorInNextLoop)
    {
        //仅仅有一种情况会调用到这里来,就是导演类调用end函数
            _purgeDirectorInNextLoop = false;
            //清除导演类
            purgeDirector();
        }
        else if (! _invalid)
        {
            //绘制
            drawScene();
            //清除内存
            PoolManager::getInstance()->getCurrentPool()->clear();
        }
    }
    

    分析的起点是mainLoop函数。这是在主线程里面会调用的循环,当中drawScene函数进行绘制。那么就进一步来看drawScene函数。


    void Director::drawScene()
    {
        //计算间隔时间
        calculateDeltaTime();
        
        //假设间隔时间过小会被忽略
        if(_deltaTime < FLT_EPSILON)
        {
            return;
        }
        //空函数,或许之后会有作用
        if (_openGLView)
        {
            _openGLView->pollInputEvents();
        }
    
        //非暂停状态
        if (! _paused)
        {
            _scheduler->update(_deltaTime);
            _eventDispatcher->dispatchEvent(_eventAfterUpdate);
        }
    
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
        //切换下一场景。必须放在逻辑后绘制前,否则会出bug
        if (_nextScene)
        {
            setNextScene();
        }
    
        kmGLPushMatrix();
        //创建单位矩阵
        kmMat4 identity;
        kmMat4Identity(&identity);
    
        //绘制场景
        if (_runningScene)
        {
            _runningScene->visit(_renderer, identity, false);
            _eventDispatcher->dispatchEvent(_eventAfterVisit);
        }
    
        //绘制观察节点,假设你须要在场景中设立观察节点,请调用摄像机的setNotificationNode函数
        if (_notificationNode)
        {
            _notificationNode->visit(_renderer, identity, false);
        }
        //绘制屏幕左下角的状态
        if (_displayStats)
        {
            showStats();
        }
        //渲染
        _renderer->render();
        //渲染后
        _eventDispatcher->dispatchEvent(_eventAfterDraw);
    
        kmGLPopMatrix();
    
        _totalFrames++;
    
        if (_openGLView)
        {
            _openGLView->swapBuffers();
        }
        //计算绘制时间
        if (_displayStats)
        {
            calculateMPF();
        }
    }
    

    当中和绘制相关的是visit的调用和render的调用,当中visit函数会调用节点的draw函数。在3.0之前的版本号中draw函数就会直接调用绘制代码。3.0版本号是在draw函数中将绘制命令存入到renderer中,然后renderer函数去进行真正的绘制。首先来看sprite的draw函数。

    void Sprite::draw(Renderer *renderer, const kmMat4 &transform, bool transformUpdated)
    {
        //检查是否超出边界,自己主动裁剪
        _insideBounds = transformUpdated ? renderer->checkVisibility(transform, _contentSize) : _insideBounds;
    
        if(_insideBounds)
        {
            //初始化
            _quadCommand.init(_globalZOrder, _texture->getName(), _shaderProgram, _blendFunc, &_quad, 1, transform);
            renderer->addCommand(&_quadCommand);
    //物理引擎相关绘制边界
    #if CC_SPRITE_DEBUG_DRAW
            _customDebugDrawCommand.init(_globalZOrder);
            //自己定义函数
            _customDebugDrawCommand.func = CC_CALLBACK_0(Sprite::drawDebugData, this);
            renderer->addCommand(&_customDebugDrawCommand);
    #endif
        }
    }
    

    这里面用了两种不同的绘制命令quadCommand初始化后就能够加入到绘制命令中,customDebugDrawCommand传入了一个回调函数,具体的命令种类会在后面介绍。当中自己定义的customDebugDrawCommand命令在初始化的时候仅仅传入了全局z轴坐标,由于它的绘制函数全部都在传入的回调函数里面。_quadCommand则须要传入全局z轴坐标,贴图名称,shader,混合,坐标点集合,坐标点集个数,变换。

    void Renderer::render()
    {
        _isRendering = true;
        
        if (_glViewAssigned)
        {
            //清除
            _drawnBatches = _drawnVertices = 0;
    
            //排序
            for (auto &renderqueue : _renderGroups)
            {
                renderqueue.sort();
            }
            //绘制
            visitRenderQueue(_renderGroups[0]);
            flush();
        }
        clean();
        _isRendering = false;
    }
    

    Render类中的render函数进行真正的绘制,首先排序,再进行绘制。从列表中的第一个组開始绘制。在visitRenderQueue函数中能够看到五种不同类型的绘制命令类型,分别相应五个类,这五个类都继承自RenderCommand。

    QUAD_COMMAND:QuadCommand类绘制精灵等。

    全部绘制图片的命令都会调用到这里,处理这个类型命令的代码就是绘制贴图的openGL代码,下一篇文章会具体介绍这部分代码。

    CUSTOM_COMMAND:CustomCommand类自己定义绘制。自己定义绘制函数,在调用绘制时仅仅需调用已经传进来的回调函数就能够,裁剪节点。绘制图形节点都採用这个绘制,把绘制函数定义在自己的类里。

    这样的类型的绘制命令不会在处理命令的时候调用不论什么一句openGL代码。而是调用你写好并设置给func的绘制函数,兴许文章会介绍引擎中的全部自己定义绘制,并自己实现一个自己定义的绘制。

    BATCH_COMMAND:BatchCommand类批处理绘制,批处理精灵和粒子

    事实上它相似于自己定义绘制,也不会再render函数中出现不论什么一句openGL函数,它调用一个固定的函数,这个函数会在下一篇文章中介绍。

    GROUP_COMMAND:GroupCommand类绘制组,一个节点包含两个以上绘制命令的时候,把这个绘制命令存储到另外一个_renderGroups中的元素中,并把这个元素的指针作为一个节点存储到_renderGroups[0]中。

    整个GROUP_COMMAND的原理须要从addCommand讲起。

    void Renderer::addCommand(RenderCommand* command)
    {
        //获得栈顶的索引
        int renderQueue =_commandGroupStack.top();
        //调用真正的addCommand
        addCommand(command, renderQueue);
    }
    
    void Renderer::addCommand(RenderCommand* command, int renderQueue)
    {
        CCASSERT(!_isRendering, "Cannot add command while rendering");
        CCASSERT(renderQueue >=0, "Invalid render queue");
        CCASSERT(command->getType() != RenderCommand::Type::UNKNOWN_COMMAND, "Invalid Command Type");
        //将命令加入到数组中
        _renderGroups[renderQueue].push_back(command);
    }
    

    addCommand有“真假”两个,差点儿全部加入渲染命令的地方,调用的都是第一个“假” addCommand,它实际上不是真正的把命令加入到_renderGroups中。它是获得须要把命令加入到_renderGroups位置中的索引。这个索引是从_commandGroupStack获得的,_commandGroupStack是个栈,当我们创建一个GROUP_COMMAND时,须要调用pushGroup函数。它是把当前这个命令在_renderGroups的索引位置压到栈顶。当addCommand时,调用top,获得这个位置

    _groupCommand.init(_globalZOrder);

    renderer->addCommand(&_groupCommand);

    renderer->pushGroup(_groupCommand.getRenderQueueID());

    GROUP_COMMAND一般用于绘制的节点有一个以上的绘制命令。把这些命令组织在一起,无需排定它们之间的顺序,他们作为一个总体被调用,所以一定要记住,栈是push,pop相应的,关于这个节点的全部的绘制命令被加入完毕后,请调用pop。将这个值从栈顶弹出,否则后面的命令也会被加入到这里。

    接下来就能够解释为什么调用的起始仅仅需调用

    visitRenderQueue(_renderGroups[0]);。为什么仅仅是0,其它的呢?

    它们会在处理GROUP_COMMAND被调用

    else if(RenderCommand::Type::GROUP_COMMAND == commandType) {
                flush();
                int renderQueueID = ((GroupCommand*) command)->getRenderQueueID();
                visitRenderQueue(_renderGroups[renderQueueID]);
    }
    

    如有错误,欢迎指出

    下一篇介绍贴图和批处理的openGL代码部分

    同一时候推荐子龙山人的openGL相关博客:http://4gamers.cn/archives/category/opengl-es-2-0

    版权声明:本文博客原创文章。博客,未经同意,不得转载。

  • 相关阅读:
    推荐阅读20100603
    [ASP.NET4之旅]Circular file references are not allowed
    满园尽是503,记曾经的一次IIS 7性能考验
    VS2010小Bug:找不到System.Web.Extensions.dll引用
    VS2010奇异Bug:三个中文符号在CSS文件中轻松让VS2010崩溃
    推荐阅读20100528
    Windows平台网站文件同步备份解决方案——cwRsyn
    推荐阅读20100803
    在IE8中使用建行企业网银的解决方法
    博客园已经用上NorthScale Memcached Server
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/4622120.html
Copyright © 2011-2022 走看看