Cocos2d-x 学习笔记(25) 渲染 绘制 Render
1. 从程序入口到渲染方法
一个Cocos2d-x项目流程中,在每一帧进行一次渲染,渲染的时机是在调度器update方法执行之后。所渲染的是当前的场景_runningScene,当前场景执行Scene::render()方法进行渲染。
在场景的渲染方法Scene::render()中,对UI树进行中序遍历,遍历到的元素执行其draw方法。对于Sprite,其draw方法是主要语句:
_trianglesCommand.init(_globalZOrder,
_texture,
getGLProgramState(),
_blendFunc,
_polyInfo.triangles,
transform,
flags); renderer->addCommand(&_trianglesCommand);
Sprite有成员变量TrianglesCommand _trianglesCommand,这是该Sprite的渲染命令。渲染命令在初始化后被添加到对应的渲染队列中。最后执行Renderer::render()方法。
从程序入口到Renderer::render()方法与渲染相关流程如下:

2. Renderer::render()
Node的draw方法只是将该Node的渲染命令加入到队列中,渲染的执行由Renderer::render()方法进行。
Renderer类有两个重要成员变量,也是两个容器:
std::stack<int> _commandGroupStack;
std::vector<RenderQueue> _renderGroups;
_commandGroupStack是存储ID的栈。
_renderGroups是存储RenderQueue的容器,RenderQueue类实质是一个存储了5种渲染命令的容器:
std::vector<RenderCommand*> _commands[QUEUE_COUNT];
该容器是渲染命令的队列,队列里的命令分为5类分别存储,每类代表不同的含义:
enum QUEUE_GROUP
{
/**Objects with globalZ smaller than 0.*/
GLOBALZ_NEG = ,
/**Opaque 3D objects with 0 globalZ.*/
OPAQUE_3D = ,
/**Transparent 3D objects with 0 globalZ.*/
TRANSPARENT_3D = ,
/**2D objects with 0 globalZ.*/
GLOBALZ_ZERO = ,
/**Objects with globalZ bigger than 0.*/
GLOBALZ_POS = ,
QUEUE_COUNT = ,
};
Renderer::render()方法的执行需要渲染命令中的一些数据,对于Sprite,它的渲染命令是TrianglesCommand。
RenderCommand是TrianglesCommand的父类。RenderCommand有成员变量枚举Type,其定义如下:
enum class Type
{
/** Reserved type.*/
UNKNOWN_COMMAND,
/** Quad command, used for draw quad.*/
QUAD_COMMAND,
/**Custom command, used for calling callback for rendering.*/
CUSTOM_COMMAND,
/**Batch command, used for draw batches in texture atlas.*/
BATCH_COMMAND,
/**Group command, which can group command in a tree hierarchy.*/
GROUP_COMMAND,
/**Mesh command, used to draw 3D meshes.*/
MESH_COMMAND,
/**Primitive command, used to draw primitives such as lines, points and triangles.*/
PRIMITIVE_COMMAND,
/**Triangles command, used to draw triangles.*/
TRIANGLES_COMMAND
};
TrianglesCommand的Type值是TRIANGLES_COMMAND。
在Sprite的draw方法中,TrianglesCommand类型的渲染命令通过init方法初始化。
该init方法首先调用父类RenderCommand的init方法设置3个变量:globalOrder, mv, flags。
之后将参数triangles赋给成员_triangles,这是一个结构体,其组成如下:
struct Triangles
{
/**Vertex data pointer.*/
V3F_C4B_T2F* verts;
/**Index data pointer.*/
unsigned short* indices;
/**The number of vertices.*/
int vertCount;
/**The number of indices.*/
int indexCount;
};
包括所有顶点数据容器、索引数据容器、顶点个数、索引个数。顶点数据使用V3F_C4B_T2F结构体存储。
接下来设置矩阵_mv、_textureID 、_blendType、_glProgramState。这里的_textureID是由参数纹理执行getName方法得到的。
init方法最后执行generateMaterialID()方法生成材质ID。该方法是通过四个变量_textureID、_blendType.src、_blendType.dst、_glProgramState,计算哈希值作为材质ID(变量_materialID)。也就是说,当两个渲染命令的四个变量完全一致时,两个渲染命令(两个Sprite)的材质才算是相同的。
Renderer::render()内的执行流程大致如下:

Renderer::render()方法对_renderGroups里的每个渲染队列执行sort方法排序,对每个队列中的TRANSPARENT_3D类型的渲染命令按Depth从小到大进行排序,对GZOrder小于0的渲染命令、GZOrder大于0的渲染命令按ZOrder从小到大进行排序。此时没有对GZOrder等于0的渲染命令排序,因为这些渲染命令的添加是按照所属的Node的LocalZOrder顺序添加的,即已经排好序,无需再次排序。
排序后执行visitRenderQueue(_renderGroups[0]),该方法是按队列里命令分类的顺序,依次对每个分类的每个命令执行processRenderCommand方法。
processRenderCommand方法里会对参数命令的Type进行判断。对于Sprite的TrianglesCommand命令,当VBO的buffer已满时,会触发drawBatchedTriangles方法;当没满时,命令会存入到Renderer容器vector<TrianglesCommand*> _queuedTriangleCommands中。
刚才讲到visitRenderQueue()方法对队列里的每个命令执行processRenderCommand()方法,主要是遍历队列内的每个分类,把命令加到容器中。在当前分类的命令都被遍历之后,执行flush()方法,该方法主要是调用了drawBatchedTriangles()方法。
drawBatchedTriangles()方法对存储命令的容器进行遍历,对每个命令的操作可分为四个步骤:

装载
每个命令执行fillVerticesAndIndices方法,填充Renderer的顶点容器_verts和索引容器_indices,具体做法是:将命令的顶点坐标转为该顶点的世界坐标,再存入到_verts中,再将命令的索引存入到_indices中,最后修改(增加)_filledVertex和_filledIndex的值。
接下来,判断批量渲染的条件是否成立,主要是比较当前命令材质ID和上个命令材质ID。如果可以进行批量绘制,把当前命令的信息加入到容器数组_triBatchesToDraw[]中,下标为上次操作的容器下标。该容器大致介绍如下:
TriBatchToDraw* _triBatchesToDraw; // Internal structure that has the information for the batches
struct TriBatchToDraw {
TrianglesCommand* cmd; // needed for the Material
GLsizei indicesToDraw;
GLsizei offset;
};
如果该命令不能进行批量绘制,则让容器数组下标加1,新容器存储当前命令的信息。
顶点和索引复制到GL缓存
很简单。_verts和_indices内的数据被复制到GL对象的缓存里。
绘制
绘制的参数是装载步骤时的TriBatchToDraw内的信息。每个TriBatchToDraw进行一次绘制,也导致了每次绘制时DrawCall的值加1。
清理
很简单,就不介绍了。
以上就是渲染的全流程解析。可以总结出,渲染是在每帧结束前进行的;渲染之前是把每帧的所有元素的绘制用命令统一进行存储,在渲染时读取这些命令,进行绘制;渲染时还会进行批量绘制的判断,这能有效降低DrawCall值。
有关降低DrawCall值的学习在这篇文章里:Cocos2d-x 学习笔记(26) 从源码学习 DrawCall 的降低方法
本文链接:https://www.cnblogs.com/deepcho/p/cocos2dx-render.html
Cocos2d-x 学习笔记(25) 渲染 绘制 Render的更多相关文章
- IOS学习笔记25—HTTP操作之ASIHTTPRequest
IOS学习笔记25—HTTP操作之ASIHTTPRequest 分类: iOS2012-08-12 10:04 7734人阅读 评论(3) 收藏 举报 iosios5网络wrapper框架新浪微博 A ...
- Unity3D学习笔记12——渲染纹理
目录 1. 概述 2. 详论 3. 问题 1. 概述 在文章<Unity3D学习笔记11--后处理>中论述了后处理是帧缓存(Framebuffer)技术实现之一:而另外一个帧缓存技术实现就 ...
- React学习笔记 - 元素渲染
React Learn Note 3 React学习笔记(三) 标签(空格分隔): React JavaScript 二.元素渲染 元素是构成react应用的最小单位. 元素是普通的对象. 元素是构成 ...
- [原创]java WEB学习笔记25:MVC案例完整实践(part 6)---新增操作的设计与实现
本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...
- linux命令学习笔记(25):linux文件属性详解
Linux 文件或目录的属性主要包括:文件或目录的节点.种类.权限模式.链接数量.所归属的用户和用户组. 最近访问或修改的时间等内容.具体情况如下: 命令: ls -lih 输出: [root@loc ...
- 【canvas学习笔记二】绘制图形
上一篇我们已经讲述了canvas的基本用法,学会了构建canvas环境.现在我们就来学习绘制一些基本图形. 坐标 canvas的坐标原点在左上角,从左到右X轴坐标增加,从上到下Y轴坐标增加.坐标的一个 ...
- 【转】cocos2d-x学习笔记03:绘制基本图元
第一部分:基本图形绘制 cocos2dx封装了大量opengl函数,用于快速绘制基本图形,这些代码的例子在,tests\DrawPrimitivesTest目录下 注意,该方法是重载node的draw ...
- Linux下汇编语言学习笔记25 ---
这是17年暑假学习Linux汇编语言的笔记记录,参考书目为清华大学出版社 Jeff Duntemann著 梁晓辉译<汇编语言基于Linux环境>的书,喜欢看原版书的同学可以看<Ass ...
- ffmpeg学习笔记-native原生绘制
上次已将ffmpeg的动态库编译出来了,并且使用了ffmpeg的转码功能,成功将mp4格式视频转化为yuv视频,这篇文章基于上次测试的demo,使用surfaceview显示解码完成的像素数据 布局设 ...
随机推荐
- 用button 属性来保存字符串地址
我用到for循环创建button 通过点击不同的按钮拿到每个button对应的链接地址,因为button的个数也是通过后台数据返回.上代码: //保存到数组 _array = [Article mj ...
- POJ - 3984 迷宫问题 (搜索)
Problem Description 定义一个二维数组: int maze[5][5] = { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, ...
- Linux 笔记 - 第十四章 LAMP 之(一) 环境搭建
博客地址:http://www.moonxy.com 一.前言 LAMP 是 Linux Apache MySQL PHP 的简写,即把 Apache.MySQL 以及 PHP 安装在 Linux 系 ...
- '\b'退格符号笔记
今天在给小孩儿讲for循环输出最后一个输出项没有空格的情况 借助标记,选择在第二个至最后一个的输出项前添加空格 ; ; i < n; i++) { ) cout << a[i]; e ...
- 修改zabbix的端口号
1.前言 zabbix-server的默认端口号是10051.如果存在端口号冲突,需要更改端口号. 以下为更改端口号的步骤. 2.更改配置文件 通常用安装包,也就是yum方式部署的话,其默认的配置文 ...
- Spring Boot (七): Mybatis极简配置
Spring Boot (七): Mybatis极简配置 1. 前言 ORM 框架的目的是简化编程中的数据库操作,经过这么多年的发展,基本上活到现在的就剩下两家了,一个是宣称可以不用写 SQL 的 H ...
- Spring 梳理 - @Autowired VS @Resource
Autowired @Autowired顾名思义,就是自动装配,其作用是为了消除代码Java代码里面的getter/setter与bean属性中的property.当然,getter看个人需求,如果私 ...
- freemarker常见语法
FreeMarker的插值有如下两种类型:1,通用插值${expr};2,数字格式化插值:#{expr}或#{expr;format} ${basketball.name?if_exists } / ...
- 详解es6 class语法糖中constructor方法和super的作用
大多数面向对象的编程语言都支持类和类继承的特性,而JS却不支持这些特性,只能通过其他方法定义并关联多个相似的对象,这种状态一直延续到了ES5.由于类似的库层出不穷,最终还是在ECMAScript 6中 ...
- 死磕 java线程系列之创建线程的8种方式
(手机横屏看源码更方便) 问题 (1)创建线程有哪几种方式? (2)它们分别有什么运用场景? 简介 创建线程,是多线程编程中最基本的操作,彤哥总结了一下,大概有8种创建线程的方式,你知道吗? 继承Th ...