【Cocos2d-x】 3.0渲染 分析之二

原文链接:

http://blog.csdn.net/bill_man/article/details/35839499

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

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void DisplayLinkDirector::mainLoop()  
  2. {  
  3.     if (_purgeDirectorInNextLoop)  
  4. {  
  5.     //只有一种情况会调用到这里来,就是导演类调用end函数  
  6.         _purgeDirectorInNextLoop = false;  
  7.         //清除导演类  
  8.         purgeDirector();  
  9.     }  
  10.     else if (! _invalid)  
  11.     {  
  12.         //绘制  
  13.         drawScene();  
  14.         //清除内存  
  15.         PoolManager::getInstance()->getCurrentPool()->clear();  
  16.     }  
  17. }  

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

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void Director::drawScene()  
  2. {  
  3.     //计算间隔时间  
  4.     calculateDeltaTime();  
  5.       
  6.     //如果间隔时间过小会被忽略  
  7.     if(_deltaTime < FLT_EPSILON)  
  8.     {  
  9.         return;  
  10.     }  
  11.     //空函数,也许之后会有作用  
  12.     if (_openGLView)  
  13.     {  
  14.         _openGLView->pollInputEvents();  
  15.     }  
  16.   
  17.     //非暂停状态  
  18.     if (! _paused)  
  19.     {  
  20.         _scheduler->update(_deltaTime);  
  21.         _eventDispatcher->dispatchEvent(_eventAfterUpdate);  
  22.     }  
  23.   
  24.     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
  25.   
  26.     //切换下一场景,必须放在逻辑后绘制前,否则会出bug  
  27.     if (_nextScene)  
  28.     {  
  29.         setNextScene();  
  30.     }  
  31.   
  32.     kmGLPushMatrix();  
  33.     //创建单位矩阵  
  34.     kmMat4 identity;  
  35.     kmMat4Identity(&identity);  
  36.   
  37.     //绘制场景  
  38.     if (_runningScene)  
  39.     {  
  40.         _runningScene->visit(_renderer, identity, false);  
  41.         _eventDispatcher->dispatchEvent(_eventAfterVisit);  
  42.     }  
  43.   
  44.     //绘制观察节点,如果你需要在场景中设立观察节点,请调用摄像机的setNotificationNode函数  
  45.     if (_notificationNode)  
  46.     {  
  47.         _notificationNode->visit(_renderer, identity, false);  
  48.     }  
  49.     //绘制屏幕左下角的状态  
  50.     if (_displayStats)  
  51.     {  
  52.         showStats();  
  53.     }  
  54.     //渲染  
  55.     _renderer->render();  
  56.     //渲染后  
  57.     _eventDispatcher->dispatchEvent(_eventAfterDraw);  
  58.   
  59.     kmGLPopMatrix();  
  60.   
  61.     _totalFrames++;  
  62.   
  63.     if (_openGLView)  
  64.     {  
  65.         _openGLView->swapBuffers();  
  66.     }  
  67.     //计算绘制时间  
  68.     if (_displayStats)  
  69.     {  
  70.         calculateMPF();  
  71.     }  
  72. }  

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

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void Sprite::draw(Renderer *renderer, const kmMat4 &transform, bool transformUpdated)  
  2. {  
  3.     //检查是否超出边界,自动裁剪  
  4.     _insideBounds = transformUpdated ? renderer->checkVisibility(transform, _contentSize) : _insideBounds;  
  5.   
  6.     if(_insideBounds)  
  7.     {  
  8.         //初始化  
  9.         _quadCommand.init(_globalZOrder, _texture->getName(), _shaderProgram, _blendFunc, &_quad, 1, transform);  
  10.         renderer->addCommand(&_quadCommand);  
  11. //物理引擎相关绘制边界  
  12. #if CC_SPRITE_DEBUG_DRAW  
  13.         _customDebugDrawCommand.init(_globalZOrder);  
  14.         //自定义函数  
  15.         _customDebugDrawCommand.func = CC_CALLBACK_0(Sprite::drawDebugData, this);  
  16.         renderer->addCommand(&_customDebugDrawCommand);  
  17. #endif  
  18.     }  
  19. }  

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

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void Renderer::render()  
  2. {  
  3.     _isRendering = true;  
  4.       
  5.     if (_glViewAssigned)  
  6.     {  
  7.         //清除  
  8.         _drawnBatches = _drawnVertices = 0;  
  9.   
  10.         //排序  
  11.         for (auto &renderqueue : _renderGroups)  
  12.         {  
  13.             renderqueue.sort();  
  14.         }  
  15.         //绘制  
  16.         visitRenderQueue(_renderGroups[0]);  
  17.         flush();  
  18.     }  
  19.     clean();  
  20.     _isRendering = false;  
  21. }  

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讲起。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void Renderer::addCommand(RenderCommand* command)  
  2. {  
  3.     //获得栈顶的索引  
  4.     int renderQueue =_commandGroupStack.top();  
  5.     //调用真正的addCommand  
  6.     addCommand(command, renderQueue);  
  7. }  
  8.   
  9. void Renderer::addCommand(RenderCommand* command, int renderQueue)  
  10. {  
  11.     CCASSERT(!_isRendering, "Cannot add command while rendering");  
  12.     CCASSERT(renderQueue >=0, "Invalid render queue");  
  13.     CCASSERT(command->getType() != RenderCommand::Type::UNKNOWN_COMMAND, "Invalid Command Type");  
  14.     //将命令加入到数组中  
  15.     _renderGroups[renderQueue].push_back(command);  
  16. }  

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被调用

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. else if(RenderCommand::Type::GROUP_COMMAND == commandType) {  
  2.             flush();  
  3.             int renderQueueID = ((GroupCommand*) command)->getRenderQueueID();  
  4.             visitRenderQueue(_renderGroups[renderQueueID]);  
  5. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值