美文网首页
cocos2d源码分析(十五):Renderer分析(一)

cocos2d源码分析(十五):Renderer分析(一)

作者: 奔向火星005 | 来源:发表于2019-05-11 18:06 被阅读0次

前面文章分析过一些cocos2d中2D的渲染流程,本文再详细补充有关cocos2d中Renderer渲染类的相关细节。

Renderer中的渲染队列

Renderer对象在Director(导演类)中,Director一般是单例,因此可以认为Renderer也是全局唯一。Renderer里面存放着渲染命令队列,它的类的简化结构如下:


再画个详细点的渲染命令队列图:


从图中可以看到,RenderQueue中定义了5种类型的渲染命令,如下代码:

class RenderQueue {
public:
    /**
    RenderCommand will be divided into Queue Groups.
    */
    enum QUEUE_GROUP
    {
        /**Objects with globalZ smaller than 0.*/
        GLOBALZ_NEG = 0,  //globalZ值小于0的物体
        /**Opaque 3D objects with 0 globalZ.*/
        OPAQUE_3D = 1,    //不透明的3D物体,globalZ都是0
        /**Transparent 3D objects with 0 globalZ.*/
        TRANSPARENT_3D = 2,  //透明的3D物体,globalZ都是0
        /**2D objects with 0 globalZ.*/
        GLOBALZ_ZERO = 3,    //globalZ为0的2D物体
        /**Objects with globalZ bigger than 0.*/
        GLOBALZ_POS = 4,     //globalZ大于0的物体
        QUEUE_COUNT = 5,
    };
//省略...
}

globalZ对应RenderCommand对象中的_globalOrder成员,渲染的顺序是_globalOrder小的先渲染,我个人的理解是,_globalOrder与我们平时理解的OpenGL中的z缓冲中的深度还是有区别的,对于2D物体而言,因为2D没有z轴,因此_globalOrder用来定义2D图像是否需要绘制,或者在绘制多个2D图像时,由谁覆盖谁。对于3D物体而言,3D物体的渲染顺序由RenderCommand的另一个成员_depth决定,_globalOrder恒为0。

渲染流程

回顾前面文章《cocos2d源码分析(四):渲染流程》中,cocos2d渲染放在主线程,流程如下:


渲染流程

我们从Renderer::render()开始继续分析,大致流程如下:


我们再详细分析下源码,Renderer::render()代码如下:

void Renderer::render()
{
    //Uncomment this once everything is rendered by new renderer
    //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //TODO: setup camera or MVP
    _isRendering = true;
    
    if (_glViewAssigned)
    {
        //Process render commands
        //1. Sort render commands based on ID
        for (auto &renderqueue : _renderGroups)
        {
            renderqueue.sort();  //对renderqueue中的命令进行排序
        }
        visitRenderQueue(_renderGroups[0]);  //执行渲染
    }
    clean();  //渲染完成后,清空_renderGroups中的所有command
    _isRendering = false;
}

其中renderqueue.sort()的函数如下:

void RenderQueue::sort()
{
    // Don't sort _queue0, it already comes sorted
    std::stable_sort(std::begin(_commands[QUEUE_GROUP::TRANSPARENT_3D]), std::end(_commands[QUEUE_GROUP::TRANSPARENT_3D]), compare3DCommand);
    std::stable_sort(std::begin(_commands[QUEUE_GROUP::GLOBALZ_NEG]), std::end(_commands[QUEUE_GROUP::GLOBALZ_NEG]), compareRenderCommand);
    std::stable_sort(std::begin(_commands[QUEUE_GROUP::GLOBALZ_POS]), std::end(_commands[QUEUE_GROUP::GLOBALZ_POS]), compareRenderCommand);
}

我们可以看到,RenderQueue::sort()中只对TRANSPARENT_3D,GLOBALZ_NEG和GLOBALZ_POS三种类型的队列进行了排序,并且使用不同的比较函数,compare3DCommand函数是比较两个command的_depth值,如下:

static bool compare3DCommand(RenderCommand* a, RenderCommand* b)
{
    return  a->getDepth() > b->getDepth();
}

compareRenderCommand函数比较的是两个command的_globalOrder值。如下:

static bool compareRenderCommand(RenderCommand* a, RenderCommand* b)
{
    return a->getGlobalOrder() < b->getGlobalOrder();
}

排序后,接着调用visitRenderQueue函数,因为代码很长,做下简化如下

void Renderer::visitRenderQueue(RenderQueue& queue)
{
    queue.saveRenderState();  //保存当前的OpenGL状态
    
   //
   //判断5种类型的队列是否存在,然后调processRenderCommand
   //如渲染不透明的3D物体
       const auto& opaqueQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::OPAQUE_3D);
    if (opaqueQueue.size() > 0)
    {
        //Clear depth to achieve layered rendering
        glEnable(GL_DEPTH_TEST);  //开启深度测试
        glDepthMask(true);
        glDisable(GL_BLEND);      //关闭混合
        glEnable(GL_CULL_FACE);   //开启背面裁剪
        RenderState::StateBlock::_defaultState->setDepthTest(true);
        RenderState::StateBlock::_defaultState->setDepthWrite(true);
        RenderState::StateBlock::_defaultState->setBlend(false);
        RenderState::StateBlock::_defaultState->setCullFace(true);

        for (const auto& opaqueNext : opaqueQueue)
        {
            processRenderCommand(opaqueNext);
        }
        flush();
    }
   //
    
    queue.restoreRenderState();  //恢复OpenGL状态
}

在visitRenderQueue函数中,因为在处理队列的过程中,可能会改变OpenGL当前的状态,因此,首先调用了queue.saveRenderState(); 它是为了保存当前的OpenGL状态,在完成队列的渲染后,再调用queue.restoreRenderState();来恢复OpenGL状态。saveRenderState函数如下:

void RenderQueue::saveRenderState()
{
    _isDepthEnabled = glIsEnabled(GL_DEPTH_TEST) != GL_FALSE;  //当前是否开启了深度测试
    _isCullEnabled = glIsEnabled(GL_CULL_FACE) != GL_FALSE;  //当前是否开启了背面裁剪
    glGetBooleanv(GL_DEPTH_WRITEMASK, &_isDepthWrite);  //当前深度缓冲区是否可写
    
    CHECK_GL_ERROR_DEBUG();
}

restoreRenderState函数如下:

void RenderQueue::restoreRenderState()
{
    if (_isCullEnabled)  //恢复之前的OpenGL状态
    {
        glEnable(GL_CULL_FACE);
        RenderState::StateBlock::_defaultState->setCullFace(true);
    }
    else
    {
        glDisable(GL_CULL_FACE);
        RenderState::StateBlock::_defaultState->setCullFace(false);
    }

    if (_isDepthEnabled)
    {
        glEnable(GL_DEPTH_TEST);
        RenderState::StateBlock::_defaultState->setDepthTest(true);
    }
    else
    {
        glDisable(GL_DEPTH_TEST);
        RenderState::StateBlock::_defaultState->setDepthTest(false);
    }
    
    glDepthMask(_isDepthWrite);
    RenderState::StateBlock::_defaultState->setDepthWrite(_isDepthEnabled);

    CHECK_GL_ERROR_DEBUG();
}

最后,processRenderCommand函数会根据不同的命令类型进行相应的渲染,在下一篇分析。

相关文章

网友评论

      本文标题:cocos2d源码分析(十五):Renderer分析(一)

      本文链接:https://www.haomeiwen.com/subject/okxxaqtx.html