cocos2dx渲染架构
2dx的时代UI树便利和渲染是没有分开的,遍历UI树的时候就渲染.3dx版本为了分离了ui树的遍历和渲染,先遍历生成渲染命令发到渲染队列,之后遍历渲染命令队列开始渲染.这样做的好处是渲染命令可以重用,单独的渲染可以做优化例如自动批绘制.本篇首先介绍cocos2D-X 3.x版本的渲染结构,之后会深入opengl es.
mainLoop
void DisplayLinkDirector::mainLoop()
{
if (_purgeDirectorInNextLoop)
{
//只有一种情况会调用到这里来,就是导演类调用end函数
_purgeDirectorInNextLoop = false;
//清除导演类
purgeDirector();
}
else if (! _invalid)
{
//绘制
drawScene();
//清除内存
PoolManager::getInstance()->getCurrentPool()->clear();
}
}
分析的起点是mainLoop函数,这是在主线程里面会调用的循环,其中drawScene函数进行绘制。那么就进一步来看drawScene函数。mainLoop实在opengl的ondrawframe调用过来的即平台每帧渲染会调用.
drawScene
void Director::drawScene()
{
//计算间隔时间
calculateDeltaTime();
//如果间隔时间过小会被忽略
if(_deltaTime < FLT_EPSILON){ return;}
//空函数,也许之后会有作用
if (_openGLView)
{
_openGLView->pollInputEvents();
}
//非暂停状态
if (! _paused)
{
//scheduler更新 会使actionmanager更新和相关的schedule更新 引擎物理模拟都是在绘制之前做的
_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)
{
//递归的遍历scene中的每个node的visit生成渲染命令放入渲染队列
_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.x之前的版本中draw函数就会直接调用绘制代码,3.x版本是在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,混合,坐标点集合,坐标点集个数,变换。
Render
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:
自定义绘制,自己定义绘制函数,在调用绘制时只需调用已经传进来的回调函数就可以,裁剪节点,绘制图形节点都采用这个绘制,把绘制函数定义在自己的类里。这种类型的绘制命令不会在处理命令的时候调用任何一句openGL代码,而是调用你写好并设置给func的绘制函数,并自己实现一个自定义的绘制。
BATCH_COMMAND:
批处理绘制,批处理精灵和粒子,其实它类似于自定义绘制,也不会再render函数中出现任何一句openGL函数,它调用一个固定的函数。
GROUP_COMMAND:
绘制组,一个节点包括两个以上绘制命令的时候,把这个绘制命令存储到另外一个renderGroups中的元素中,并把这个元素的指针作为一个节点存储到renderGroups[0]中。
render流程
void Renderer::addCommand(RenderCommand* command)
{
//获得栈顶的索引
int renderQueue =_commandGroupStack.top();
//调用真正的addCommand
addCommand(command, renderQueue);
}
void Renderer::addCommand(RenderCommand* command, int renderQueue)
{
//将命令加入到数组中
_renderGroups[renderQueue].push_back(command);
}
addCommand它是获得需要把命令加入到renderGroups位置中的索引,这个索引是从commandGroupStack获得的,commandGroupStack是个栈,当我们创建一个GROUP_COMMAND时,需要调用pushGroup函数,它是把当前这个命令在_renderGroups的索引位置压到栈顶,当addCommand时,调用top,获得这个位置
groupCommand.init(globalZOrder);
renderer->addCommand(&_groupCommand);
renderer->pushGroup(_groupCommand.getRenderQueueID());
GROUP_COMMAND一般用于绘制的节点有一个以上的绘制命 令,把这些命令组织在一起,无需排定它们之间的顺序,他们作为一个整体被调用,所以一定要记住,栈是push,pop对应的,关于这个节点的所有的绘制命令被添加完成后,请调用pop,将这个值从栈顶弹出,否则后面的命令也会被添加到这里。
为什么调用的起始只需调用为什么只是0,其他的呢?
visitRenderQueue(_renderGroups[0]);
它们会在处理GROUP_COMMAND被调用
else if(RenderCommand::Type::GROUP_COMMAND == commandType) {
flush();
int renderQueueID = ((GroupCommand*) command)->getRenderQueueID();
visitRenderQueue(_renderGroups[renderQueueID]);
}
cocos2dx渲染架构的更多相关文章
- cocos2D-X从的源代码的分析cocos2D-X学习OpenGL(1)----cocos2D-X渲染架构
个人原创.欢迎转载,转载请注明原文地址http://blog.csdn.net/bill_man 从本篇文章開始,将分析cocos2D-X 3.0源码,第一部分是从cocos2D-X学习OpenGL ...
- 将Cocos2dX渲染到MFC窗口上
引用:http://www.cnblogs.com/windeer/archive/2012/11/18/2767750.html 引言 现在智能手机已经慢慢进入大众化,移动类应用开始火爆起来,游戏类 ...
- Prerender.io - 预渲染架构,提高AngularJS SEO
近些年来,越来越多的JavaScript框架(即AngularJS,BackboneJS,ReactJS)变得越来越流行.许多公司和开发人员使用这些JavaScript框架开发应用程序.这些框架有很多 ...
- 如何让手游更省带宽,耗电量更少?TBR渲染架构解析!
如何让手游更省带宽,耗电量更少?渲染或是其中一个可突破的点.本文中,腾讯游戏学院专家Hailong将从为大家解析TBR渲染架构的特点. 什么是TBR? 全称是Tile Based Rendering, ...
- CesiumJS 2022^ 原理[2] 渲染架构之三维物体 - 创建并执行指令
目录 回顾 预备知识:指令 预备知识:通道 1. 生成并执行指令 1.1. Primitive 生成指令 1.2. Context 对象负责执行 WebGL 底层代码 2. 多段视锥体技术 3. 指令 ...
- cocos2d-x渲染流程
Cocos2Dx之渲染流程 发表于8个月前(2014-08-08 22:46) 阅读(3762) | 评论(2) 17人收藏此文章, 我要收藏 赞2 如何快速提高你的薪资?-实力拍“跳槽吧兄弟”梦 ...
- 拆分Cocos2dx渲染部分代码
纹理实现 思想 这个是Cocos2dx的渲染部分的最基本的实现,被我拆分到mac上,但是并不是用的EGLContext,而是搭配glfw,还有soil第三方图形库. 实现 // // main.cpp ...
- 使用Node搭建reactSSR服务端渲染架构
如题:本文所讲架构主要用到技术栈有:Node, Express, React, Mobx, webpack4, ES6, ES7, axios, ejs, log4js, scss,echarts, ...
- 拆分Cocos2dx 渲染项目 总结
因为只拆分了渲染的内容,所以代码只针对渲染部分进行分析. 代码涉及到这些类: CCImage,对图片的数据进行操作 CCNode,CCSprite,结点类 CCProgram,CCRenderer,C ...
随机推荐
- Python中的分组函数(groupby、itertools)
from operator import itemgetter #itemgetter用来去dict中的key,省去了使用lambda函数 from itertools import groupby ...
- MySQL面试之说明myisam和innodb两种存储引擎的不同之处
1.事务的支持不同(innodb支持事务,myisam不支持事务) 2.锁粒度(innodb行锁应用,myisam表锁) 3.存储空间(innodb既缓存索引文件又缓存数据文件,myisam只能缓存索 ...
- Docker容器学习与分享06
Docker容器网络 Docker有三种原生网络:none网络.host网络.bridge网络. 先来学习一下bridge网络. 首先使用ifconfig命令查看一下本机的网络设备: 从图中可以看见多 ...
- sql行列转换PIVOT与unPIVOT
基本语法 select * from Mould pivot ( count(ID)for ProductTypeCode in ( [FC], [RCU], [RCD] )) as PVT; wit ...
- Mapreduce运行过程分析(基于Hadoop2.4)——(二)
4.3 Map类 创建Map类和map函数.map函数是org.apache.hadoop.mapreduce.Mapper类中的定义的,当处理每一个键值对的时候,都要调用一次map方法,用户须 ...
- 【转】通过blob获取图像并显示
HTML代码: <div id="forAppend" class="demo"></div> JS代码: var eleAppend ...
- oracle 更新用户密码,授连接权限,
1.授连接权限 grant connect to 用户名; ALTER USER 用户名 ACCOUNT UNLOCK; 2.更新密码 ALTER USER 用户名 IDENTIFIED BY 更新密 ...
- 使用java实现hex和ascii码的转换
几乎很少写JAVA代码,第一是确实不会,第二感觉JAVA写起来不爽(较python.golang),但总有万不得已必须要用java的时候.这里记录下使用java实现的hex十六进制和acsii码之间的 ...
- auto关键字使用
auto类型变量--根据初始值推断真实的数据类型. 有些时候并不能很确定一个变量应该具备的数据类型,例如:将一个复杂表达式的值赋给某个变量,此时并不能很明显的确定这个值所具备的数据类型.此时auto关 ...
- python生成语谱图
语音的时域分析和频域分析是语音分析的两种重要方法,但是都存在着局限性.时域分析对语音信号的频率特性没有直观的了解,频域特性中又没有语音信号随时间的变化关系.而语谱图综合了时域和频域的优点,明显的显示出 ...