cocos2D-X源码分析之从cocos2D-X学习OpenGL(1)----cocos2D-X渲染结构

前端之家收集整理的这篇文章主要介绍了cocos2D-X源码分析之从cocos2D-X学习OpenGL(1)----cocos2D-X渲染结构前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

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

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

  1. void@H_301_3@DisplayLinkDirector::mainLoop()@H_301_3@@H_301_3@
  2. {@H_301_3@
  3. if@H_301_3@(_purgeDirectorInNextLoop)@H_301_3@@H_301_3@
  4. //只有一种情况会调用到这里来,就是导演类调用end函数@H_301_3@@H_301_3@@H_301_3@
  5. _purgeDirectorInNextLoop=false@H_301_3@;@H_301_3@@H_301_3@
  6. //清除导演类@H_301_3@@H_301_3@@H_301_3@
  7. purgeDirector();@H_301_3@
  8. }@H_301_3@
  9. else@H_301_3@@H_301_3@if@H_301_3@(!_invalid)@H_301_3@@H_301_3@
  10. {@H_301_3@
  11. //绘制@H_301_3@@H_301_3@@H_301_3@
  12. drawScene();@H_301_3@
  13. //清除内存@H_301_3@@H_301_3@@H_301_3@
  14. PoolManager::getInstance()->getCurrentPool()->clear();@H_301_3@
  15. }@H_301_3@
  16. }@H_301_3@

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

    void@H_301_3@Director::drawScene()@H_301_3@@H_301_3@
  1. //计算间隔时间@H_301_3@@H_301_3@@H_301_3@
  2. calculateDeltaTime();@H_301_3@
  3. @H_301_3@
  4. //如果间隔时间过小会被忽略@H_301_3@@H_301_3@@H_301_3@
  5. if@H_301_3@(_deltaTime<FLT_EPSILON)@H_301_3@@H_301_3@
  6. return@H_301_3@;@H_301_3@@H_301_3@
  7. //空函数,也许之后会有作用@H_301_3@@H_301_3@@H_301_3@
  8. if@H_301_3@(_openGLView)@H_301_3@@H_301_3@
  9. _openGLView->pollInputEvents();@H_301_3@
  10. @H_301_3@
  11. //非暂停状态@H_301_3@@H_301_3@@H_301_3@
  12. if@H_301_3@(!_paused)@H_301_3@@H_301_3@
  13. _scheduler->update(_deltaTime);@H_301_3@
  14. _eventDispatcher->dispatchEvent(_eventAfterUpdate);@H_301_3@
  15. glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);@H_301_3@
  16. //切换下一场景,必须放在逻辑后绘制前,否则会出bug@H_301_3@@H_301_3@@H_301_3@
  17. if@H_301_3@(_nextScene)@H_301_3@@H_301_3@
  18. setNextScene();@H_301_3@
  19. kmGLPushMatrix();@H_301_3@
  20. //创建单位矩阵@H_301_3@@H_301_3@@H_301_3@
  21. kmMat4identity;@H_301_3@
  22. kmMat4Identity(&identity);@H_301_3@
  23. //绘制场景@H_301_3@@H_301_3@@H_301_3@
  24. if@H_301_3@(_runningScene)@H_301_3@@H_301_3@
  25. _runningScene->visit(_renderer,identity,false@H_301_3@);@H_301_3@@H_301_3@
  26. _eventDispatcher->dispatchEvent(_eventAfterVisit);@H_301_3@
  27. //绘制观察节点,如果你需要在场景中设立观察节点,请调用摄像机的setNotificationNode函数@H_301_3@@H_301_3@@H_301_3@
  28. if@H_301_3@(_notificationNode)@H_301_3@@H_301_3@
  29. _notificationNode->visit(_renderer,153); font-weight:bold; background-color:inherit">false@H_301_3@);@H_301_3@@H_301_3@
  30. //绘制屏幕左下角的状态@H_301_3@@H_301_3@@H_301_3@
  31. if@H_301_3@(_displayStats)@H_301_3@@H_301_3@
  32. showStats();@H_301_3@
  33. //渲染@H_301_3@@H_301_3@@H_301_3@
  34. _renderer->render();@H_301_3@
  35. //渲染后@H_301_3@@H_301_3@@H_301_3@
  36. _eventDispatcher->dispatchEvent(_eventAfterDraw);@H_301_3@
  37. kmGLPopMatrix();@H_301_3@
  38. _totalFrames++;@H_301_3@
  39. if@H_301_3@(_openGLView)@H_301_3@@H_301_3@
  40. _openGLView->swapBuffers();@H_301_3@
  41. //计算绘制时间@H_301_3@@H_301_3@@H_301_3@
  42. calculateMPF();@H_301_3@
  43. }@H_301_3@

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

    void@H_301_3@Sprite::draw(Renderer*renderer,@H_301_3@const@H_301_3@kmMat4&transform,@H_301_3@bool@H_301_3@transformUpdated)@H_301_3@@H_301_3@
  1. //检查是否超出边界,自动裁剪@H_301_3@@H_301_3@@H_301_3@
  2. _insideBounds=transformUpdated?renderer->checkVisibility(transform,_contentSize):_insideBounds;@H_301_3@
  3. if@H_301_3@(_insideBounds)@H_301_3@@H_301_3@
  4. //初始化@H_301_3@@H_301_3@@H_301_3@
  5. _quadCommand.init(_globalZOrder,_texture->getName(),_shaderProgram,_blendFunc,&_quad,1,transform);@H_301_3@
  6. renderer->addCommand(&_quadCommand);@H_301_3@
  7. //物理引擎相关绘制边界@H_301_3@@H_301_3@@H_301_3@
  8. #ifCC_SPRITE_DEBUG_DRAW@H_301_3@@H_301_3@@H_301_3@
  9. _customDebugDrawCommand.init(_globalZOrder);@H_301_3@
  10. //自定义函数@H_301_3@@H_301_3@@H_301_3@
  11. _customDebugDrawCommand.func=CC_CALLBACK_0(Sprite::drawDebugData,153); font-weight:bold; background-color:inherit">this@H_301_3@);@H_301_3@@H_301_3@
  12. renderer->addCommand(&_customDebugDrawCommand);@H_301_3@
  13. #endif@H_301_3@@H_301_3@@H_301_3@
  14. 这里面用了两种不同的绘制命令quadCommand初始化后就可以加入到绘制命令中,customDebugDrawCommand传入了一个回调函数,具体的命令种类会在后面介绍。其中自定义的customDebugDrawCommand命令在初始化的时候只传入了全局z轴坐标,因为它的绘制函数全部都在传入的回调函数里面,_quadCommand则需要传入全局z轴坐标,贴图名称,shader,混合,坐标点集合,坐标点集个数,变换。

    void@H_301_3@Renderer::render()@H_301_3@@H_301_3@
  1. _isRendering=true@H_301_3@;@H_301_3@@H_301_3@
  2. if@H_301_3@(_glViewAssigned)@H_301_3@@H_301_3@
  3. //清除@H_301_3@@H_301_3@@H_301_3@
  4. _drawnBatches=_drawnVertices=0;@H_301_3@
  5. //排序@H_301_3@@H_301_3@@H_301_3@
  6. for@H_301_3@(auto&renderqueue:_renderGroups)@H_301_3@@H_301_3@
  7. renderqueue.sort();@H_301_3@
  8. //绘制@H_301_3@@H_301_3@@H_301_3@
  9. visitRenderQueue(_renderGroups[0]);@H_301_3@
  10. flush();@H_301_3@
  11. clean();@H_301_3@
  12. _isRendering= 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@H_301_3@Renderer::addCommand(RenderCommand*command)@H_301_3@@H_301_3@
  1. //获得栈顶的索引@H_301_3@@H_301_3@@H_301_3@
  2. int@H_301_3@renderQueue=_commandGroupStack.top();@H_301_3@@H_301_3@
  3. //调用真正的addCommand@H_301_3@@H_301_3@@H_301_3@
  4. addCommand(command,renderQueue);@H_301_3@
  5. void@H_301_3@Renderer::addCommand(RenderCommand*command,87); font-weight:bold; background-color:inherit">int@H_301_3@renderQueue)@H_301_3@@H_301_3@
  6. CCASSERT(!_isRendering,"Cannotaddcommandwhilerendering"@H_301_3@);@H_301_3@@H_301_3@
  7. CCASSERT(renderQueue>=0,"Invalidrenderqueue"@H_301_3@);@H_301_3@@H_301_3@
  8. CCASSERT(command->getType()!=RenderCommand::Type::UNKNOWN_COMMAND,"InvalidCommandType"@H_301_3@);@H_301_3@@H_301_3@
  9. //将命令加入到数组中@H_301_3@@H_301_3@@H_301_3@
  10. _renderGroups[renderQueue].push_back(command);@H_301_3@
  11. }@H_301_3@

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

    if@H_301_3@(RenderCommand::Type::GROUP_COMMAND==commandType){@H_301_3@@H_301_3@
  1. flush();@H_301_3@
  2. int@H_301_3@renderQueueID=((GroupCommand*)command)->getRenderQueueID();@H_301_3@@H_301_3@
  3. visitRenderQueue(_renderGroups[renderQueueID]);@H_301_3@
  4. 如有错误,欢迎指出

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

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

    猜你在找的Cocos2d-x相关文章