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

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

void DisplayLinkDirector::mainLoop()
{
if (_purgeDirectorInNextLoop)
{
//仅仅有一种情况会调用到这里来,就是导演类调用end函数
_purgeDirectorInNextLoop = false;
//清除导演类
purgeDirector();
}
else if (! _invalid)
{
//绘制
drawScene();
//清除内存
PoolManager::getInstance()->getCurrentPool()->clear();
}
}

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

void Director::drawScene()
{
//计算间隔时间
calculateDeltaTime(); //假设间隔时间过小会被忽略
if(_deltaTime < FLT_EPSILON)
{
return;
}
//空函数,或许之后会有作用
if (_openGLView)
{
_openGLView->pollInputEvents();
} //非暂停状态
if (! _paused)
{
_scheduler->update(_deltaTime);
_eventDispatcher->dispatchEvent(_eventAfterUpdate);
} glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //切换下一场景。必须放在逻辑后绘制前,否则会出bug
if (_nextScene)
{
setNextScene();
} kmGLPushMatrix();
//创建单位矩阵
kmMat4 identity;
kmMat4Identity(&identity); //绘制场景
if (_runningScene)
{
_runningScene->visit(_renderer, identity, false);
_eventDispatcher->dispatchEvent(_eventAfterVisit);
} //绘制观察节点,假设你须要在场景中设立观察节点,请调用摄像机的setNotificationNode函数
if (_notificationNode)
{
_notificationNode->visit(_renderer, identity, false);
}
//绘制屏幕左下角的状态
if (_displayStats)
{
showStats();
}
//渲染
_renderer->render();
//渲染后
_eventDispatcher->dispatchEvent(_eventAfterDraw); kmGLPopMatrix(); _totalFrames++; if (_openGLView)
{
_openGLView->swapBuffers();
}
//计算绘制时间
if (_displayStats)
{
calculateMPF();
}
}

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

void Sprite::draw(Renderer *renderer, const kmMat4 &transform, bool transformUpdated)
{
//检查是否超出边界,自己主动裁剪
_insideBounds = transformUpdated ? renderer->checkVisibility(transform, _contentSize) : _insideBounds; if(_insideBounds)
{
//初始化
_quadCommand.init(_globalZOrder, _texture->getName(), _shaderProgram, _blendFunc, &_quad, 1, transform);
renderer->addCommand(&_quadCommand);
//物理引擎相关绘制边界
#if CC_SPRITE_DEBUG_DRAW
_customDebugDrawCommand.init(_globalZOrder);
//自己定义函数
_customDebugDrawCommand.func = CC_CALLBACK_0(Sprite::drawDebugData, this);
renderer->addCommand(&_customDebugDrawCommand);
#endif
}
}

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

void Renderer::render()
{
_isRendering = true; if (_glViewAssigned)
{
//清除
_drawnBatches = _drawnVertices = 0; //排序
for (auto &renderqueue : _renderGroups)
{
renderqueue.sort();
}
//绘制
visitRenderQueue(_renderGroups[0]);
flush();
}
clean();
_isRendering = false;
}

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 Renderer::addCommand(RenderCommand* command)
{
//获得栈顶的索引
int renderQueue =_commandGroupStack.top();
//调用真正的addCommand
addCommand(command, renderQueue);
} void Renderer::addCommand(RenderCommand* command, int renderQueue)
{
CCASSERT(!_isRendering, "Cannot add command while rendering");
CCASSERT(renderQueue >=0, "Invalid render queue");
CCASSERT(command->getType() != RenderCommand::Type::UNKNOWN_COMMAND, "Invalid Command Type");
//将命令加入到数组中
_renderGroups[renderQueue].push_back(command);
}

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

else if(RenderCommand::Type::GROUP_COMMAND == commandType) {
flush();
int renderQueueID = ((GroupCommand*) command)->getRenderQueueID();
visitRenderQueue(_renderGroups[renderQueueID]);
}

如有错误,欢迎指出

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

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

版权声明:本文博客原创文章。博客,未经同意,不得转载。

cocos2D-X从的源代码的分析cocos2D-X学习OpenGL(1)----cocos2D-X渲染架构的更多相关文章

  1. Ffmpeg解析media容器过程/ ffmpeg 源代码简单分析 : av_read_frame()

    ffmpeg 源代码简单分析 : av_read_frame() http://blog.csdn.net/leixiaohua1020/article/details/12678577 ffmpeg ...

  2. C C++源代码安全分析工具调研

    C C++源代码安全分析工具调研:http://blog.csdn.net/testing_is_believing/article/details/22047107

  3. Linux内核源代码情景分析系列

    http://blog.sina.com.cn/s/blog_6b94d5680101vfqv.html Linux内核源代码情景分析---第五章 文件系统  5.1 概述 构成一个操作系统最重要的就 ...

  4. 《Android系统源代码情景分析》连载回忆录:灵感之源

    上个月,在花了一年半时间之后,写了55篇文章,分析完成了Chromium在Android上的实现,以及Android基于Chromium实现的WebView.学到了很多东西,不过也挺累的,平均不到两个 ...

  5. FFmpeg的HEVC解码器源代码简单分析:环路滤波(Loop Filter)

    ===================================================== HEVC源代码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpe ...

  6. FFmpeg的HEVC解码器源代码简单分析:CTU解码(CTU Decode)部分-TU

    ===================================================== HEVC源代码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpe ...

  7. FFmpeg的HEVC解码器源代码简单分析:CTU解码(CTU Decode)部分-PU

    ===================================================== HEVC源代码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpe ...

  8. FFmpeg的HEVC解码器源代码简单分析:解码器主干部分

    ===================================================== HEVC源代码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpe ...

  9. FFmpeg的HEVC解码器源代码简单分析:解析器(Parser)部分

    ===================================================== HEVC源代码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpe ...

  10. FFmpeg的HEVC解码器源代码简单分析:概述

    ===================================================== HEVC源代码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpe ...

随机推荐

  1. 如何在电脑上播放iso映像文件

    http://blog.sina.com.cn/s/blog_4a20485e0102e5ya.html

  2. Hadoop基本原理之一:MapReduce 分类: A1_HADOOP 2014-08-17 19:26 1113人阅读 评论(0) 收藏

    1.为什么需要Hadoop 目前,一块硬盘容量约为1TB,读取速度约为100M/S,因此完成一块硬盘的读取需时约2.5小时(写入时间更长).若把数据放在同一硬盘上,且全部数据均需要同一个程序进行处理, ...

  3. 段的创建表user_segments 分类: H2_ORACLE 2013-08-10 11:13 714人阅读 评论(0) 收藏

    1.段的定义及类型 Oracle中的段(segment)是占用磁盘空间的一个对象,最常见的段类型包括: l  聚簇cluster l  表table l  表分区 tablepartition l  ...

  4. margin隐藏最后的切割线

    <style> *{margin: 0;padding: 0;} #demo li{ border-bottom: 1px solid #ccc; } #demo ul{ margin-b ...

  5. [Compose] 18. Maintaining structure whilst asyncing

    We take our Promise.all() analogy further by using traversable on a Map(). Then we use two traversal ...

  6. 【转】priority_queue的用法

    http://www.cnblogs.com/flyoung2008/articles/2136485.html priority_queue调用 STL里面的 make_heap(), pop_he ...

  7. 【u031】租用游艇

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 长江游艇俱乐部在长江上设置了n 个游艇出租站1,2,-,n.游客可在这些游艇出租站租用游艇,并在下游的 ...

  8. 【u027】神秘大三角

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 判断一个点与已知三角形的位置关系. [输入格式] 前三行:每行一个坐标,表示该三角形的三个顶点 第四行 ...

  9. while 常见程序逻辑

    1. 查找 List L; Position P = L; while (P && P->Element != Key) { P = P->Next; } return P ...

  10. list数组归并去重

    C#两路list数组归并去重 个相同类型已排序数据进行合并,虽然list数组中有AddRange方法,但它只是把第二个数组从第一个数组末尾插入,假如两个数组有重复数据,保存进去.还有Union方法合并 ...