日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

cocos渲染流程

發布時間:2025/3/15 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 cocos渲染流程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

最近在研究Cocos引擎的渲染流程,在這里將其整個渲染流程進行一下梳理:

梳理之前我們要知道一些東西,就是我們的Cocos引擎是通過使用OpenGL的一些API來進行渲染繪制的,所以如果我們要徹底理解Cocos引擎的渲染流程并想修改引擎底層渲染的相關內容,熟悉OpenGL是很有必要的。

這里先簡單說一下大概流程,Cocos3.x版本的渲染是將所有需要渲染的node先通過各種RenderCommand封裝起來,你先不用管RenderCommand是什么,只需要記住它把我們要渲染的node封裝起來了就行,然后引擎把這些RenderCommand添加到了一個隊列中存了起來,這個隊列叫CommandQueue,添加的時候順便對這些RenderCommand設置了一些參數,最后在每一幀結束時調用進行渲染,渲染前會根據ID對RenderCommand進行排序,然后再進行渲染。


?

?

好了接下來我們來開始梳理引擎整個的渲染流程了:

首先,整個工程的渲染流程的入口在哪里呢?

我們打開工程文件目錄,在?platform\win32文件目錄下找到CCApplication-win3類文件,這里要注意不同平臺的不一樣,比如mac平臺下是platform\mac目錄下的CCApplication-mac文件,根據我們發布的工程平臺的不同,這個CCApplication類文件也不同。整個渲染流程就在這個CCApplication類文件run()方法中開始,代碼如下:


[cpp] view plain copy print?

  • int?Application::run()??

  • {?????

  • ????......?????????

  • ????director->mainLoop();//進入引擎的主循環??

  • ????......??????

  • ????return?0;??

  • }??

  • int?Application::run() {???......???????director->mainLoop();//進入引擎的主循環......????return?0; }

    這里我們要了解一個概念,就是cocos2dx整個工程是運行在一個單線程里的,也就是我們經常說的主線程,在主線程里完成渲染、相關的定時器等等處理。注意Application::run()中的這句:


    [cpp] view plain copy print?

  • director->mainLoop();??

  • director->mainLoop();


    這句代碼就是進入cocos2d-x的主循環了,這個主循環mainLoop()由導演負責維護,主線程mainloop()會不停地執行,理想狀態下每秒會調用60次。

    那我們看看CCDirector類里的mainLoop()方法具體做了些什么:


    [cpp] view plain copy print?

  • void?DisplayLinkDirector::mainLoop()??

  • {??

  • ????if?(_purgeDirectorInNextLoop)//進入下一個主循環,也就是結束這次的主循環,就凈化,也就是一些后期處理???

  • ????{??

  • ????????_purgeDirectorInNextLoop?=?false;??

  • ????????purgeDirector();??

  • ????}??

  • ????else?if?(_restartDirectorInNextLoop)??

  • ????{??

  • ????????_restartDirectorInNextLoop?=?false;??

  • ????????restartDirector();??

  • ????}??

  • ????else?if?(!?_invalid)??

  • ????{??

  • ????????drawScene();//繪制屏幕??

  • ????????PoolManager::getInstance()->getCurrentPool()->clear();//釋放一些沒有用的對象,主要保件內存的合理管理???

  • ????}??

  • }??

  • void?DisplayLinkDirector::mainLoop() {if?(_purgeDirectorInNextLoop)//進入下一個主循環,也就是結束這次的主循環,就凈化,也就是一些后期處理?{_purgeDirectorInNextLoop?=?false;purgeDirector();}else?if?(_restartDirectorInNextLoop){_restartDirectorInNextLoop?=?false;restartDirector();}else?if?(!?_invalid){drawScene();//繪制屏幕PoolManager::getInstance()->getCurrentPool()->clear();//釋放一些沒有用的對象,主要保件內存的合理管理?} }

    最開始我還疑惑為什么mainLoop()方法的類是DisplayLinkDirector而不是CCDirector,但是在CCDirector.cpp中我們會找到如下代碼:


    [cpp] view plain copy print?

  • static?DisplayLinkDirector?*s_SharedDirector?=?nullptr;??

  • Director*?Director::getInstance()??

  • {??

  • ????if?(!s_SharedDirector)??

  • ????{??

  • ????????s_SharedDirector?=?new?(std::nothrow)?DisplayLinkDirector();??

  • ????????CCASSERT(s_SharedDirector,?"FATAL:?Not?enough?memory");??

  • ????????s_SharedDirector->init();??

  • ????}??

  • ???

  • ????return?s_SharedDirector;??

  • }??

  • static?DisplayLinkDirector?*s_SharedDirector?=?nullptr; Director*?Director::getInstance() {if?(!s_SharedDirector){s_SharedDirector?=?new?(std::nothrow)?DisplayLinkDirector();CCASSERT(s_SharedDirector,?"FATAL:?Not?enough?memory");s_SharedDirector->init();}return?s_SharedDirector; }

    我們可以看到Director類返回的單例對象是一個DisplayLinkDirector類型的,所以這個導演實例要執行mainLoop()方法,這個方法自然是DisplayLinkDirector類里的方法啦!

    但是這是不是說明Director類就是DisplayLinkDirector類或繼承自DisplayLinkDirector類呢?千萬不要這樣想!這兩個類沒有半毛錢關系,我們在CCDirector.h中看到如下代碼:

    [cpp] view plain copy print?

  • class?CC_DLL?Director?:?public?Ref??

  • class?CC_DLL?Director?:?public?Ref


    可以看出Director類是繼承自Ref類的,只是通過getInstance()方法返回的導演類的實例對象是DisplayLinkDirector類型的,CCDisplayLinkDirector類是CCDisplay的子類,從命名就應該可以很清晰的知道它的用處。這里雖然有點繞,但不要混淆哈!


    好了,回過頭來,在DisplayLinkDirector::mainLoop()方法中我可以看到這句代碼:


    [cpp] view plain copy print?

  • void?DisplayLinkDirector::mainLoop()??

  • {??

  • ????......??

  • ????drawScene();??

  • ????......??

  • }??

  • void?DisplayLinkDirector::mainLoop() {......drawScene();...... }

    mainloop()如果執行會調用drawScene(),通過drawScene()代碼就可以實現場景的繪制了。

    那我們繼續看看drawScene()具體做了些什么:

    [cpp] view plain copy print?

  • void?Director::drawScene()??

  • {??

  • ????......??

  • ????if?(_notificationNode)??

  • ???{??

  • ????????_notificationNode->visit(_renderer,?Mat4::IDENTITY,?0);??

  • ???}??

  • ????......??

  • ????_renderer->render();??

  • }??

  • void?Director::drawScene() {......if?(_notificationNode){_notificationNode->visit(_renderer,?Mat4::IDENTITY,?0);}......_renderer->render(); }

    Director::drawScene()做了好多事情,其他的先不看,我們主要關注這兩句:

    [cpp] view plain copy print?

  • 1._notificationNode->visit(_renderer,?Mat4::IDENTITY,?0);??

  • 1._notificationNode->visit(_renderer,?Mat4::IDENTITY,?0);


    [cpp] view plain copy print?

  • 2._renderer->render();??

  • 2._renderer->render();


    先看第一句,這句_notificationNode->visit(_renderer,?Mat4::IDENTITY,?0)?,這句其實是進入了一個循環調用,具體要看CCNode.cpp

    [cpp] view plain copy print?

  • void?Node::visit(Renderer*?renderer,?const?Mat4?&parentTransform,?uint32_t?parentFlags)??

  • {??

  • ????......???

  • ????????for(?;?i?<?_children.size();?i++?)??

  • ????????{??

  • ????????????auto?node?=?_children.at(i);??

  • ???

  • ????????????if?(node?&&?node->_localZOrder?<?0)??

  • ????????????????node->visit(renderer,?_modelViewTransform,?flags);??

  • ????????????else??

  • ????????????????break;??

  • ????????}??

  • ????????......??

  • ????????this->draw(renderer,?_modelViewTransform,?flags);??

  • ????????......??

  • }??

  • void?Node::visit(Renderer*?renderer,?const?Mat4?&parentTransform,?uint32_t?parentFlags) {......?for(?;?i?<?_children.size();?i++?){auto?node?=?_children.at(i);if?(node?&&?node->_localZOrder?<?0)node->visit(renderer,?_modelViewTransform,?flags);elsebreak;}......this->draw(renderer,?_modelViewTransform,?flags);...... }

    這個函數有一個循環調用,我們可以看到auto?node?=?_children.at(i);和node->visit(renderer,?_modelViewTransform,?flags);,這段代碼的意思是先獲取子節點,然后遞歸調用節點的visit()函數,到了沒有子節點的節點,執行了這句this->draw(renderer,?_modelViewTransform,?flags),開始調用draw()函數,那么我們接著看draw()函數代碼:

    [cpp] view plain copy print?

  • void?Node::draw(Renderer*?renderer,?const?Mat4?&transform,?uint32_t?flags)??

  • {??

  • }??

  • void?Node::draw(Renderer*?renderer,?const?Mat4?&transform,?uint32_t?flags) { }

    里面什么都沒有啊,這是怎么回事?其實這個draw()函數是個虛函數,所以它執行時執行的是該子節點類的draw()函數。那么我們分別看DrawNode::draw()Sprite::draw()

    [cpp] view plain copy print?

  • void?DrawNode::draw(Renderer?*renderer,?const?Mat4?&transform,?uint32_t?flags)??

  • {??

  • ????if(_bufferCount)??

  • ????{??

  • ????????......??

  • ????????renderer->addCommand(&_customCommand);??

  • ????}??

  • ????if(_bufferCountGLPoint)??

  • ????{??

  • ????????......??

  • ????????renderer->addCommand(&_customCommandGLPoint);??

  • ????}??

  • ??????

  • ????if(_bufferCountGLLine)??

  • ????{??

  • ????????......??

  • ????????renderer->addCommand(&_customCommandGLLine);??

  • ????}??

  • }??

  • void?DrawNode::draw(Renderer?*renderer,?const?Mat4?&transform,?uint32_t?flags) {if(_bufferCount){......renderer->addCommand(&_customCommand);}if(_bufferCountGLPoint){......renderer->addCommand(&_customCommandGLPoint);}if(_bufferCountGLLine){......renderer->addCommand(&_customCommandGLLine);} }

    [cpp] view plain copy print?

  • void?Sprite::draw(Renderer?*renderer,?const?Mat4?&transform,?uint32_t?flags)??

  • {??

  • ......??

  • ????if(_insideBounds)??

  • {??

  • ????......??

  • ????????renderer->addCommand(&_trianglesCommand);??

  • ????}??

  • }??

  • void?Sprite::draw(Renderer?*renderer,?const?Mat4?&transform,?uint32_t?flags) { ......if(_insideBounds) {......renderer->addCommand(&_trianglesCommand);} }

    我們可以看到在在這些子類的draw()函數都執行了renderer->addCommand()代碼,這是向RenderQueue中添加RenderCommand,在添加時順便對RenderCommand進行了一些參數設置,當然有的類的draw()不是向RenderQueue中添加RenderCommand,而是直接使用OpenGL的API直接進行渲染,或者做一些其他的事情。


    當Director::drawScene()循環調用完所有子節點的visit()方法并且執行完draw()方法,即向RenderQueue中添加完RenderCommand后,我們就看看接下來進行渲染的Renderer::render()?函數都做了些什么:

    [cpp] view plain copy print?

  • void?Renderer::render()??

  • {??

  • ????_isRendering?=?true;??

  • ??????

  • ????if?(_glViewAssigned)??

  • ????{??

  • ????????for?(auto?&renderqueue?:?_renderGroups)??

  • ????????{??

  • ????????????renderqueue.sort();??

  • ????????}??

  • ????????visitRenderQueue(_renderGroups[0]);??

  • ????}??

  • ????clean();??

  • ????_isRendering?=?false;??

  • }??

  • void?Renderer::render() {_isRendering?=?true;if?(_glViewAssigned){for?(auto?&renderqueue?:?_renderGroups){renderqueue.sort();}visitRenderQueue(_renderGroups[0]);}clean();_isRendering?=?false; }

    看到“renderqueue.sort()",這是根據ID先對所有RenderCommand進行排序,然后才進行渲染,“visitRenderQueue(?_renderGroups[0])”就是來進行渲染的。

    那么我們接著看看void?Renderer::visitRenderQueue(const?RenderQueue&?queue)的代碼:

    [cpp] view plain copy print?

  • void?Renderer::visitRenderQueue(RenderQueue&?queue)??

  • {??

  • ????queue.saveRenderState();??

  • ????const?auto&?zNegQueue?=?queue.getSubQueue(RenderQueue::QUEUE_GROUP::GLOBALZ_NEG);??

  • ????if?(zNegQueue.size()?>?0)??

  • ????{??

  • ????????if(_isDepthTestFor2D)??

  • ????????{??

  • ????????????glEnable(GL_DEPTH_TEST);??

  • ????????????glDepthMask(true);??

  • ????????????glEnable(GL_BLEND);??

  • ????????????RenderState::StateBlock::_defaultState->setDepthTest(true);??

  • ????????????RenderState::StateBlock::_defaultState->setDepthWrite(true);??

  • ????????????RenderState::StateBlock::_defaultState->setBlend(true);??

  • ????????}??

  • ????????else??

  • ????????{??

  • ????????????glDisable(GL_DEPTH_TEST);??

  • ????????????glDepthMask(false);??

  • ????????????glEnable(GL_BLEND);??

  • ????????????RenderState::StateBlock::_defaultState->setDepthTest(false);??

  • ????????????RenderState::StateBlock::_defaultState->setDepthWrite(false);??

  • ????????????RenderState::StateBlock::_defaultState->setBlend(true);??

  • ????????}??

  • ????????for?(auto?it?=?zNegQueue.cbegin();?it?!=?zNegQueue.cend();?++it)??

  • ????????{??

  • ????????????proce***enderCommand(*it);??

  • ????????}??

  • ????????flush();??

  • }??

  • void?Renderer::visitRenderQueue(RenderQueue&?queue) {queue.saveRenderState();const?auto&?zNegQueue?=?queue.getSubQueue(RenderQueue::QUEUE_GROUP::GLOBALZ_NEG);if?(zNegQueue.size()?>?0){if(_isDepthTestFor2D){glEnable(GL_DEPTH_TEST);glDepthMask(true);glEnable(GL_BLEND);RenderState::StateBlock::_defaultState->setDepthTest(true);RenderState::StateBlock::_defaultState->setDepthWrite(true);RenderState::StateBlock::_defaultState->setBlend(true);}else{glDisable(GL_DEPTH_TEST);glDepthMask(false);glEnable(GL_BLEND);RenderState::StateBlock::_defaultState->setDepthTest(false);RenderState::StateBlock::_defaultState->setDepthWrite(false);RenderState::StateBlock::_defaultState->setBlend(true);}for?(auto?it?=?zNegQueue.cbegin();?it?!=?zNegQueue.cend();?++it){proce***enderCommand(*it);}flush(); }

    在visitRenderQueue()方法中我我們看到這一行代碼:

    [cpp] view plain copy print?

  • proce***enderCommand(*it);??

  • proce***enderCommand(*it);


    這是干什么的呢?這句代碼就是進一步進入渲染流程的,我們看一下proce***enderCommand()它做了什么:

    [cpp] view plain copy print?

  • void?Renderer::proce***enderCommand(RenderCommand*?command)??

  • {??

  • ????auto?commandType?=?command->getType();??

  • ????if(?RenderCommand::Type::TRIANGLES_COMMAND?==?commandType)??

  • ????{??

  • ?????????......??

  • ?????????drawBatchedTriangles();??

  • ?????????......??

  • ????}??

  • ????else?if?(?RenderCommand::Type::QUAD_COMMAND?==?commandType?)??

  • ????{??

  • ????????......??

  • ????????drawBatchedQuads();??

  • ????????......??

  • ????}??

  • ????else?if?(RenderCommand::Type::MESH_COMMAND?==?commandType)??

  • ????{??

  • ????????......??

  • ????????auto?cmd?=?static_cast<MeshCommand*>(command);??

  • ????????......??

  • ????????cmd->execute();??

  • ????????......??

  • ????}??

  • ????......??

  • }??

  • void?Renderer::proce***enderCommand(RenderCommand*?command) {auto?commandType?=?command->getType();if(?RenderCommand::Type::TRIANGLES_COMMAND?==?commandType){......drawBatchedTriangles();......}else?if?(?RenderCommand::Type::QUAD_COMMAND?==?commandType?){......drawBatchedQuads();......}else?if?(RenderCommand::Type::MESH_COMMAND?==?commandType){......auto?cmd?=?static_cast<MeshCommand*>(command);......cmd->execute();......}...... }

    我們可以看到,在這里,根據渲染類型的不同,會調用不同的函數,這些函數里有OpenGL的API,沒錯,這些函數來進行渲染的。比如TRIANGLES_COMMAND類型中調用了drawBatchedTriangles(),QUAD_COMMAND類型中調用了drawBatchedQuads(),MESH_COMMAND類型中調用了MeshCommand::execute(),等等。

    舉個例子,我們來看下drawBatchedTriangles()方法

    [cpp] view plain copy print?

  • void?Renderer::drawBatchedTriangles()??

  • {??

  • ????......??

  • ????if?(Configuration::getInstance()->supportsShareableVAO())??

  • ????{??

  • ????????......}??

  • ????else??

  • ????{??

  • ????????......??

  • ????????//?vertices??

  • ????????glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION,?3,?GL_FLOAT,?GL_FALSE,?kQuadSize,?(GLvoid*)?offsetof(V3F_C4B_T2F,?vertices));??

  • ???

  • ????????//?colors??

  • ????????glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR,?4,?GL_UNSIGNED_BYTE,?GL_TRUE,?kQuadSize,?(GLvoid*)?offsetof(V3F_C4B_T2F,?colors));??

  • ???

  • ????????//?tex?coords??

  • ????????glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD,?2,?GL_FLOAT,?GL_FALSE,?kQuadSize,?(GLvoid*)?offsetof(V3F_C4B_T2F,?texCoords));??

  • ???

  • ????????glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,?_buffersVBO[1]);??

  • ????????glBufferData(GL_ELEMENT_ARRAY_BUFFER,?sizeof(_indices[0])?*?_filledIndex,?_indices,?GL_STATIC_DRAW);??

  • ??????}??

  • ???????......??

  • }??

  • void?Renderer::drawBatchedTriangles() {......if?(Configuration::getInstance()->supportsShareableVAO()){......}else{......//?verticesglVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION,?3,?GL_FLOAT,?GL_FALSE,?kQuadSize,?(GLvoid*)?offsetof(V3F_C4B_T2F,?vertices));//?colorsglVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR,?4,?GL_UNSIGNED_BYTE,?GL_TRUE,?kQuadSize,?(GLvoid*)?offsetof(V3F_C4B_T2F,?colors));//?tex?coordsglVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD,?2,?GL_FLOAT,?GL_FALSE,?kQuadSize,?(GLvoid*)?offsetof(V3F_C4B_T2F,?texCoords));glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,?_buffersVBO[1]);glBufferData(GL_ELEMENT_ARRAY_BUFFER,?sizeof(_indices[0])?*?_filledIndex,?_indices,?GL_STATIC_DRAW);}...... }

    可以看到該方法中調用了很多OpenGL的API,這些方法就是整個渲染流程最后進行渲染的環節。

    ?

    好了,以上便是Cocos引擎的整個的渲染流程了。

    最后用一個流程圖對以上內容做一下總結,話說這張圖我真的是很用心畫的,改了好多遍最后優化到現在這個樣子給大家看,希望對大家有幫助:


    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    以上。


    轉載于:https://blog.51cto.com/12525470/1934434

    總結

    以上是生活随笔為你收集整理的cocos渲染流程的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。