cocos2d-x 3.6版本学习笔记-渲染过程

姑且从Application::run()开始吧。代码1:

int Application::run()
{
    PVRFrameEnableControlWindow(false);//设置注册表PVRFrame隐藏 

    // Main message loop:
    LARGE_INTEGER nLast;
    LARGE_INTEGER nNow;

    QueryPerformanceCounter(&nLast); //获取当前的计数值,即频率x当前时间 

	initGLContextAttrs();//设置 context 属性,目前只能设置6个属性 red,green,blue,alpha,depth(深度),stencil(模板),详见GLViewImpl::initWithRect

    // Initialize instance and cocos2d.
    if (!applicationDidFinishLaunching())
    {
        return 1;
    }

    auto director = Director::getInstance();
    auto glview = director->getOpenGLView();

    // Retain glview to avoid glview being released in the while loop
    glview->retain();

    while(!glview->windowShouldClose())
    {
        QueryPerformanceCounter(&nNow);// 取得当前的计数值,即频率x当前时间 
        if (nNow.QuadPart - nLast.QuadPart > _animationInterval.QuadPart)	// 每隔_animationInterval.QuadPart时间绘制一次
        {
            nLast.QuadPart = nNow.QuadPart - (nNow.QuadPart % _animationInterval.QuadPart);
            
            director->mainLoop();
            glview->pollEvents();
        }
        else
        {
            Sleep(1);
        }
    }

    // Director should still do a cleanup if the window was closed manually.
    if (glview->isOpenGLReady())	// 判断glView窗口是否创建成功
    {
        director->end();		// 将结束标志设为真,再做一次循环
        director->mainLoop();	// 此交循环中将会对资源进行清理,释放
        director = nullptr;
    }
    glview->release();
    return 0;
}

渲染之前,先整理一下Director对象,因为Director做了opengl渲染的一些准备工作。创建代码如下,代码2:

Director* Director::getInstance()
{
    if (!s_SharedDirector)
    {
        s_SharedDirector = new (std::nothrow) DisplayLinkDirector(); // new(std::nothrow)表示当创建失败时不抛出异常,直接返回NULL
        CCASSERT(s_SharedDirector, "FATAL: Not enough memory");
        s_SharedDirector->init();
    }

    return s_SharedDirector;
}

init函数,代码3:

bool Director::init(void)
{
    setDefaultValues();

    ...

    //init TextureCache
    initTextureCache();	// 构造_textureCache对象
    initMatrixStack();	// 初始化_modelViewMatrixStack,_projectionMatrixStack,_textureMatrixStack三个矩阵

    _renderer = new (std::nothrow) Renderer;	// 构造render对象

    return true;
}

setDefaultValues函数,代码4:

void Director::setDefaultValues(void)
{
    Configuration *conf = Configuration::getInstance();


    // default FPS
    double fps = conf->getValue("cocos2d.x.fps", Value(kDefaultFPS)).asDouble();
    _oldAnimationInterval = _animationInterval = 1.0 / fps;


    // 是滞显示 FPS
    _displayStats = conf->getValue("cocos2d.x.display_fps", Value(false)).asBool();


    // GL projection,2d 和 3D,在设置视景体时有区别,默认是3D
    std::string projection = conf->getValue("cocos2d.x.gl.projection", Value("3d")).asString();
    if (projection == "3d")
        _projection = Projection::_3D;
    else if (projection == "2d")
        _projection = Projection::_2D;
    else if (projection == "custom")
        _projection = Projection::CUSTOM;
    else
        CCASSERT(false, "Invalid projection value");


<span style="white-space:pre">	</span>/*RGBA8888,RGBA4444,RGB5A1:为3种像素格式,详见百度。*/
    // Default pixel format for PNG images with alpha
    std::string pixel_format = conf->getValue("cocos2d.x.texture.pixel_format_for_png", Value("rgba8888")).asString();
    if (pixel_format == "rgba8888")
        Texture2D::setDefaultAlphaPixelFormat(Texture2D::PixelFormat::RGBA8888);
    else if(pixel_format == "rgba4444")
        Texture2D::setDefaultAlphaPixelFormat(Texture2D::PixelFormat::RGBA4444);
    else if(pixel_format == "rgba5551")
        Texture2D::setDefaultAlphaPixelFormat(Texture2D::PixelFormat::RGB5A1);


    // PVR v2 has alpha premultiplied ?
    bool pvr_alpha_premultipled = conf->getValue("cocos2d.x.texture.pvrv2_has_alpha_premultiplied", Value(false)).asBool();
    Image::setPVRImagesHavePremultipliedAlpha(pvr_alpha_premultipled);
}
setDefaultValues设置了FPS,是否显示FPS,投影类型,像素格式等参数。所有的参数都是通过Configuration对象获取的,除此之外,Configuration配置的参数还包括:所支持的MaxTextureSize, MaxModelviewStackDepth,  MaxTextureUnits;NPOT(2的幂);  PVRTC,ETC,S3TC压缩标准,  BGRA8888纹理格式;  glDiscardFramebufferEXT;  VAO; MaxSupportDirLight, GLExtension.。Configuration的实现这里不展开了。

下面来整理一下具体的渲染过程,进入director->mainLoop(),代码如下,代码5:

void DisplayLinkDirector::mainLoop()
{
    if (_purgeDirectorInNextLoop) //只有一种情况会调用到这里来,就是导演类调用end函数
    {
        _purgeDirectorInNextLoop = false;
        purgeDirector();	// 停止场景的所有动作,移除所有监听事件,释放相关的资源
    }
    else if (_restartDirectorInNextLoop)
    {
        _restartDirectorInNextLoop = false;
        restartDirector();	 // 先销毁,再重新创建所有对象
    }
    else if (! _invalid)	// 能否进行下一次渲染,如果开始动画(startAnimation),则渲染
    {
        drawScene();
     
        // release the objects
        PoolManager::getInstance()->getCurrentPool()->clear();
    }
}

mainLoop函数通过purge,restart几个标志位控制着渲染的暂停,结束,具体的渲染工作在drawScene中完成。

drawScene函数代码如下,代码6:

void Director::drawScene()
{
    // calculate "global" dt
    calculateDeltaTime();//计算间隔时间
    
    if (_openGLView)
    {
        _openGLView->pollEvents();<span style="white-space:pre">	</span>// 调用到glfwPollEvents()分发事件
    }


    //tick before glClear: issue #533
    if (! _paused)
    {
        _scheduler->update(_deltaTime);
        _eventDispatcher->dispatchEvent(_eventAfterUpdate);
    }


    _renderer->clear();//清除buffer和depth_buffer


    /* to avoid flickr, nextScene MUST be here: after tick and before draw.
     * FIXME: Which bug is this one. It seems that it can't be reproduced with v0.9
     */
    if (_nextScene)<span style="white-space:pre">	</span>// 如果场景有更换的话,更换场景
    {
        setNextScene();// 清理当前场景_runningScene,将_nextScene设置为_runningScene
    }


    pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);//将模型矩阵、投影矩阵、纹理矩阵重置,注:此三个矩阵栈里均有两个Identity矩阵,一个用于计算,一个作为母本
    
    if (_runningScene)
    {
#if CC_USE_PHYSICS
        auto physicsWorld = _runningScene->getPhysicsWorld();
        if (physicsWorld && physicsWorld->isAutoStep())
        {
            physicsWorld->update(_deltaTime, false);
        }
#endif
        //clear draw stats
        _renderer->clearDrawStats();<span style="white-space:pre">	</span>//_drawnBatches = _drawnVertices = 0;
        
        //render the scene
        _runningScene->render(_renderer);//绘制运行的场景,包括组装command和执行command,注:每执行完一次,render都会清理一遍command
        
        _eventDispatcher->dispatchEvent(_eventAfterVisit);
    }


    // draw the notifications node
    if (_notificationNode)// 注:此节点不会随场景更换而移除
    {
        _notificationNode->visit(_renderer, Mat4::IDENTITY, 0);//如果有悬浮节点,则组装command到_renderer命令队列中
    }


    if (_displayStats)// 是否显示状态信息,即FPS等信息
    {
        showStats();
    }
    _renderer->render();//执行命令队列


    _eventDispatcher->dispatchEvent(_eventAfterDraw);


    popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);


    _totalFrames++;


    // swap buffers
    if (_openGLView)
    {
        _openGLView->swapBuffers();
    }


    if (_displayStats)
    {
        calculateMPF();
    }
}
drawscene中,要渲染的内容分两部分,一部分是_runningScene,另一部分是_notificationNode(如果显示的话),_notificationNode只是一些测试数据,这里不展开。主要看_runningScene->render(_renderer),代码如下,代码7:

void Scene::render(Renderer* renderer)
{
    auto director = Director::getInstance();
    Camera* defaultCamera = nullptr;
    const auto& transform = getNodeToParentTransform(); // 获取从子节点坐标转换到父节点坐标的矩阵
    if (_cameraOrderDirty)
    {
        stable_sort(_cameras.begin(), _cameras.end(), camera_cmp);
        _cameraOrderDirty = false;
    }
    
    for (const auto& camera : _cameras) // 注:当_cameras在onEnter的时候会被绑定相应的scene,并加入到scene的_cameras列表中
    {
        if (!camera->isVisible())
            continue;
        
        Camera::_visitingCamera = camera;
        if (Camera::_visitingCamera->getCameraFlag() == CameraFlag::DEFAULT)
        {
            defaultCamera = Camera::_visitingCamera;
        }
        
        director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
        director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, Camera::_visitingCamera->getViewProjectionMatrix());
        
        //visit the scene 
        visit(renderer, transform, 0);	// 遍历场景和场景子节点并组装对应的渲染命令,并添加到render的命令列表
        renderer->render();				// 遍历渲染列表,并依次执行命令,实现渲染
        
        director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
    }

    Camera::_visitingCamera = nullptr;
}
在render函数里,就可以看出一点opengl的影子了,最起码 设置矩阵->渲染代码->重置矩阵 这个过程大家应该很熟悉。下面再接着往里扒,进入visit函数,代码8:

void Node::visit(Renderer* renderer, const Mat4 &parentTransform, uint32_t parentFlags)
{
    // quick return if not visible. children won't be drawn.
    if (!_visible)
    {
        return;
    }

    uint32_t flags = processParentFlags(parentTransform, parentFlags);

    // IMPORTANT:
    // To ease the migration to v3.0, we still support the Mat4 stack,
    // but it is deprecated and your code should not rely on it
    _director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
    _director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform);
    
    bool visibleByCamera = isVisitableByVisitingCamera();

    int i = 0;

    if(!_children.empty())
    {
        sortAllChildren();
        // draw children zOrder < 0
        for( ; i < _children.size(); i++ )
        {
            auto node = _children.at(i);

            if (node && node->_localZOrder < 0)
                node->visit(renderer, _modelViewTransform, flags);
            else
                break;
        }
        // self draw, node的draw什么也没做,但它是一个虚函数,实现在他的子类里面,完成的绘制命令的组装,添加添加到renderer的命令列表中(一个node对应0到多个命令对象,具体的命令定义在node的子类中)
        if (visibleByCamera)
            this->draw(renderer, _modelViewTransform, flags);

        for(auto it=_children.cbegin()+i; it != _children.cend(); ++it)
            (*it)->visit(renderer, _modelViewTransform, flags);
    }
    else if (visibleByCamera)
    {
        this->draw(renderer, _modelViewTransform, flags);
    }

    _director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
    
    // FIX ME: Why need to set _orderOfArrival to 0??
    // Please refer to https://github.com/cocos2d/cocos2d-x/pull/6920
    // reset for next frame
    // _orderOfArrival = 0;
}
visit函数递归调用了自己,重点在draw函数中,node的draw函数什么也没做,但它是一个虚函数,其子类是有实现的,以sprite为例,代码9:

void Sprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
#if CC_USE_CULLING
    // Don't do calculate the culling if the transform was not updated
    _insideBounds = (flags & FLAGS_TRANSFORM_DIRTY) ? renderer->checkVisibility(transform, _contentSize) : _insideBounds;

    if(_insideBounds)
#endif
    {
        _quadCommand.init(_globalZOrder, _texture->getName(), getGLProgramState(), _blendFunc, &_quad, 1, transform, flags);// 构造渲染命令对象
        renderer->addCommand(&_quadCommand);
        
#if CC_SPRITE_DEBUG_DRAW
        _debugDrawNode->clear();
        Vec2 vertices[4] = {
            Vec2( _quad.bl.vertices.x, _quad.bl.vertices.y ),
            Vec2( _quad.br.vertices.x, _quad.br.vertices.y ),
            Vec2( _quad.tr.vertices.x, _quad.tr.vertices.y ),
            Vec2( _quad.tl.vertices.x, _quad.tl.vertices.y ),
        };
        _debugDrawNode->drawPoly(vertices, 4, true, Color4F(1.0, 1.0, 1.0, 1.0));
#endif //CC_SPRITE_DEBUG_DRAW
    }
}

sprite的draw函数主要就是init了一个_quadCommand命令对象,并添加到了renderer的命令列表中,可见,node->draw就际上就是一个构造rendercommand命令的过程,还没有任何实质的渲染行为。具体的命令对象的结构是什么这里不扒了,可以去看opengl vao,vbo,3.6版本里有6种渲染命令:TRIANGLES_COMMAND,QUAD_COMMAND,MESH_COMMAND,GROUP_COMMAND,CUSTOM_COMMAND,BATCH_COMMAND,PRIMITIVE_COMMAND,这些命令稍后再提。

下面回到代码7,visit函数遍历各node组装好渲染命令,并添加到renderer的命令列表中后,开始调用renderer->render();执行命令,代码如下,代码10:

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();
        }
        visitRenderQueue(_renderGroups[0]);
    }
    clean();
    _isRendering = false;
}
void Renderer::visitRenderQueue(RenderQueue& queue)
{
    queue.saveRenderState();<span style="white-space:pre">	</span>// 获取GL_DEPTH_TEST,GL_CULL_FACE和GL_DEPTH_WRITEMASK开关标志,为了绘制完后决定是否需要关闭这些开关,见restoreRenderState
    
    //
    //Process Global-Z < 0 Objects,需要做混合
    //
    const auto& zNegQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::GLOBALZ_NEG);
    if (zNegQueue.size() > 0)
    {
        if(_isDepthTestFor2D)
        {
            glEnable(GL_DEPTH_TEST);
            glDepthMask(true);
        }
        else
        {
            glDisable(GL_DEPTH_TEST);
            glDepthMask(false);
        }
        for (auto it = zNegQueue.cbegin(); it != zNegQueue.cend(); ++it)
        {
            processRenderCommand(*it);
        }
        flush();
    }
    
    //
    //Process Opaque Object
    //
    const auto& opaqueQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::OPAQUE_3D);
    if (opaqueQueue.size() > 0)
    {
        //Clear depth to achieve layered rendering
        glDepthMask(true);
        glEnable(GL_DEPTH_TEST);
        
        for (auto it = opaqueQueue.cbegin(); it != opaqueQueue.cend(); ++it)
        {
            processRenderCommand(*it);
        }
        flush();
    }
    
    //
    //Process 3D Transparent object
    //
    const auto& transQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::TRANSPARENT_3D);
    if (transQueue.size() > 0)
    {
        glEnable(GL_DEPTH_TEST);
        glDepthMask(false);
        
        for (auto it = transQueue.cbegin(); it != transQueue.cend(); ++it)
        {
            processRenderCommand(*it);
        }
        flush();
    }
    
    //
    //Process Global-Z = 0 Queue
    //
    const auto& zZeroQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::GLOBALZ_ZERO);
    if (zZeroQueue.size() > 0)
    {
        if(_isDepthTestFor2D)
        {
            glEnable(GL_DEPTH_TEST);
            glDepthMask(true);
        }
        else
        {
            glDisable(GL_DEPTH_TEST);
            glDepthMask(false);
        }
        for (auto it = zZeroQueue.cbegin(); it != zZeroQueue.cend(); ++it)
        {
            processRenderCommand(*it);
        }
        flush();
    }
    
    //
    //Process Global-Z > 0 Queue
    //
    const auto& zPosQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::GLOBALZ_POS);
    if (zPosQueue.size() > 0)
    {
        for (auto it = zPosQueue.cbegin(); it != zPosQueue.cend(); ++it)
        {
            processRenderCommand(*it);
        }
        flush();
    }
    
    queue.restoreRenderState();
}
void Renderer::processRenderCommand(RenderCommand* command)
{
    auto commandType = command->getType();
    if( RenderCommand::Type::TRIANGLES_COMMAND == commandType)
    {
        //Draw if we have batched other commands which are not triangle command
        flush3D();
        flushQuads();
        
        //Process triangle command
        auto cmd = static_cast<TrianglesCommand*>(command);
        
        //Draw batched Triangles if necessary
        if(cmd->isSkipBatching() || _filledVertex + cmd->getVertexCount() > VBO_SIZE || _filledIndex + cmd->getIndexCount() > INDEX_VBO_SIZE)
        {
            CCASSERT(cmd->getVertexCount()>= 0 && cmd->getVertexCount() < VBO_SIZE, "VBO for vertex is not big enough, please break the data down or use customized render command");
            CCASSERT(cmd->getIndexCount()>= 0 && cmd->getIndexCount() < INDEX_VBO_SIZE, "VBO for index is not big enough, please break the data down or use customized render command");
            //Draw batched Triangles if VBO is full
            drawBatchedTriangles();
        }
        
        //Batch Triangles
        _batchedCommands.push_back(cmd);
        
        fillVerticesAndIndices(cmd);
        
        if(cmd->isSkipBatching())
        {
            drawBatchedTriangles();
        }
        
    }
    else if ( RenderCommand::Type::QUAD_COMMAND == commandType )
    {
        //Draw if we have batched other commands which are not quad command
        flush3D();
        flushTriangles();
        
        //Process quad command
        auto cmd = static_cast<QuadCommand*>(command);
        
        //Draw batched quads if necessary
        if(cmd->isSkipBatching()|| (_numberQuads + cmd->getQuadCount()) * 4 > VBO_SIZE )
        {
            CCASSERT(cmd->getQuadCount()>= 0 && cmd->getQuadCount() * 4 < VBO_SIZE, "VBO for vertex is not big enough, please break the data down or use customized render command");
            //Draw batched quads if VBO is full
            drawBatchedQuads();
        }
        
        //Batch Quads
        _batchQuadCommands.push_back(cmd);
        
        fillQuads(cmd);
        
        if(cmd->isSkipBatching())
        {
            drawBatchedQuads();
        }
    }
    else if (RenderCommand::Type::MESH_COMMAND == commandType)
    {
        flush2D();
        auto cmd = static_cast<MeshCommand*>(command);
        
        if (cmd->isSkipBatching() || _lastBatchedMeshCommand == nullptr || _lastBatchedMeshCommand->getMaterialID() != cmd->getMaterialID())
        {
            flush3D();
            
            if(cmd->isSkipBatching())
            {
                cmd->execute();
            }
            else
            {
                cmd->preBatchDraw();
                cmd->batchDraw();
                _lastBatchedMeshCommand = cmd;
            }
        }
        else
        {
            cmd->batchDraw();
        }
    }
    else if(RenderCommand::Type::GROUP_COMMAND == commandType)
    {
        flush();
        int renderQueueID = ((GroupCommand*) command)->getRenderQueueID();
        visitRenderQueue(_renderGroups[renderQueueID]);
    }
    else if(RenderCommand::Type::CUSTOM_COMMAND == commandType)
    {
        flush();
        auto cmd = static_cast<CustomCommand*>(command);
        cmd->execute();
    }
    else if(RenderCommand::Type::BATCH_COMMAND == commandType)
    {
        flush();
        auto cmd = static_cast<BatchCommand*>(command);
        cmd->execute();
    }
    else if(RenderCommand::Type::PRIMITIVE_COMMAND == commandType)
    {
        flush();
        auto cmd = static_cast<PrimitiveCommand*>(command);
        cmd->execute();
    }
    else
    {
        CCLOGERROR("Unknown commands in renderQueue");
    }
}
再这里,一次渲染过程结束。

最后,我们现再来看看6个renderercommand类型(细节还没有深入研究,等研究了再整理吧。。)

TRIANGLES_COMMAND(详见TrianglesCommand类):

用于渲染三角形,renderer类中专门有drawBatchedTriangles来批量处理该命令。


QUAD_COMMAND(详见QuadCommand类):

用于渲染4边形,与TRIANGLES_COMMAND类似,renderer类中专门也有drawBatchedQuads来批量处理该命令。


MESH_COMMAND(详见MeshCommand类)。

GROUP_COMMAND(详见GroupCommand类)。

CUSTOM_COMMAND(详见CustomCommand类),可以绑定一个回调函数如:

_customCommand.init(_globalZOrder);
_customCommand.func = CC_CALLBACK_0(DrawTest::onDraw, this, transform,flags);


BATCH_COMMAND(详见BatchCommand类)。

PRIMITIVE_COMMAND(详见BatchCommand类)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值