渲染,感觉这个挺重要了,这里代入一个简单的例子 Sprite 建立及到最后的画在屏幕上, 我们描述一下这个渲染的流程:
 
1 sprite 初始化(纹理, 坐标,及当前元素的坐标大小信息)
2 主循环调用sprite的draw(), 把绘制命令发送到系统的render的渲染队列中。 
3 Render拿到渲染队列中的渲染命令, 分别对每个命令进行处理, 我们这里的QUAD_COMMAND, 把这个命令中的坐标信息复制到自己的渲染缓冲中, 之后通过调用OpenGL命令对当前的矩形进行绘制。 

 
1 Sprite初始化:
 
如:
    Size visibleSize = Director::getInstance()->getVisibleSize();
Sprite *sp = Sprite::create("HelloWorld.png");
sp->setPosition(Point(visibleSize.width * ., visibleSize.height * .));
this->addChild(sp, );
 
1.1 简述:
 
1> 初始化Sprite, 从纹理缓存中读取纹理的大小及相关信息。 
2> 初始化当前对象中的_quad属性,Sprite对OpenGL来说就是一个矩形的绘制,要绘制它, 就需要它的顶点信息,纹理信息, 这些信息都在Sprite初始化时被添加到了_quad属性中。 具体看一下下面的这个属性的数据结构。 
 
1.2 详细:
 
1> 初始化Sprite 
 
做了一堆的初始化工作这里我们注意一下_quad. 这个对象记录了当前的Sprite, 矩形的坐标,及纹理相关的信息。我们之后的绘制就需要这个对象。 
void Sprite::setTextureRect(const Rect& rect, bool rotated, const Size& untrimmedSize)

1. _quad

V3F_C4B_T2F_Quad _quad;
V3F_C4B_T2F_Quad, 这个数据结构记录了我们的左上,左下,右上, 右下,这个矩形的4个顶点信息, 每个顶点是由一个V3F_C4B_T2F结构组成的, 可以看到,其中有坐标, 颜色, 纹理的数据存储。
 
//! 4 Vertex3FTex2FColor4B
struct CC_DLL V3F_C4B_T2F_Quad
{
//! top left
V3F_C4B_T2F tl;
//! bottom left
V3F_C4B_T2F bl;
//! top right
V3F_C4B_T2F tr;
//! bottom right
V3F_C4B_T2F br;
}; //! a Vec2 with a vertex point, a tex coord point and a color 4B
struct CC_DLL V3F_C4B_T2F
{
//! vertices (3F)
Vec3 vertices; // 12 bytes //! colors (4B)
Color4B colors; // 4 bytes // tex coords (2F)
Tex2F texCoords; // 8 bytes
};
 
2. 纹理相关的初始化 setTextureCoords()
 
     _quad.bl.texCoords.u = left;
_quad.bl.texCoords.v = bottom;
_quad.br.texCoords.u = right;
_quad.br.texCoords.v = bottom;
_quad.tl.texCoords.u = left;
_quad.tl.texCoords.v = top;
_quad.tr.texCoords.u = right;
_quad.tr.texCoords.v = top;
3.  坐标相关初始化

  // Atlas: Vertex
float x1 = + _offsetPosition.x;
float y1 = + _offsetPosition.y;
float x2 = x1 + _rect.size.width;
float y2 = y1 + _rect.size.height; // Don't update Z.
_quad.bl.vertices = Vec3(x1, y1, );
_quad.br.vertices = Vec3(x2, y1, );
_quad.tl.vertices = Vec3(x1, y2, );
_quad.tr.vertices = Vec3(x2, y2, );
2 调用Sprite draw() 
 
还记得之前一篇的主循环吗?
 
Director 每帧都调用drawScene(), drawScene()中会调用当前Scene->render() , Scene->render() 又会递归遍历其子元素,分别调用子元素的visit() 或 draw() , 当有子元素时, 用visit() , 没有子元素时直接调用其draw. 我们这里的Sprite被调用了, 这里调用的是draw(). 
 
这个函数, 主要做了一件事,初始化一个矩形绘制命令, 把当前绘制命令发送到当前的系统 Render 的绘制队列中。

void Sprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
// Don't do calculate the culling if the transform was not updated
_insideBounds = (flags & FLAGS_TRANSFORM_DIRTY) ? renderer->checkVisibility(transform, _contentSize) : _insideBounds; if(_insideBounds)
{
_quadCommand.init(_globalZOrder, _texture->getName(), getGLProgramState(), _blendFunc, &_quad, , transform);
renderer->addCommand(&_quadCommand);
}
3 Render:render() 
 
Render内部保存了一个命令的绘制队列,cocos2dx的所有绘制都会被封装成为命令发送到当前的绘制队列中。
 
当整个的子节点遍历后, 即所有的绘制命令加入到绘制队列上时, 这里调用 render->render()
这个函数的作用是绘制当前绘制队列中的绘制信息, 最终是调用OpenGL的命令完成了,绘制。 
下面看看它是怎么做的。 
 
 
3.1 排序, 对绘制命令进行排序
 
这里默认使用的遍历顺序排序. 当然也提供了可以改变这个顺序的方法,  这里不展开。 
 
3.2 遍历当前的绘制队列
 
命令的类型:
TRIANGLES_COMMAND
QUAD_COMMAND
GROUP_COMMAND
CUSTOM_COMMAND
BATCH_COMMAND
PRIMITIVE_COMMAND
MESH_COMMAND
1》根据不同的命令类型来对命令进行处理, 这里我们只关注,QUAD_COMMAND, 之前我们的Sprite就发送的这种类型的绘制命令。
 

else if ( RenderCommand::Type::QUAD_COMMAND == commandType )
{
flush3D();
if(_filledIndex > )
{
drawBatchedTriangles();
_lastMaterialID = ;
}
auto cmd = static_cast<QuadCommand*>(command);
//Batch quads
if( (_numberQuads + cmd->getQuadCount()) * > VBO_SIZE )
{
CCASSERT(cmd->getQuadCount()>= && cmd->getQuadCount() * < VBO_SIZE, "VBO for vertex is not big enough, please break the data down or use customized render command");
//Draw batched quads if VBO is full
drawBatchedQuads();
} _batchQuadCommands.push_back(cmd); fillQuads(cmd);
}
 
有2个关键点,
 
1 把绘制命令加到到批绘制列表中。_batchQuadCommands.push_back(cmd)
 
2 处理当前的命令中的顶点数据, fillQuads(cmd) 

void Renderer::fillQuads(const QuadCommand *cmd)
{
memcpy(_quadVerts + _numberQuads * , cmd->getQuads(), sizeof(V3F_C4B_T2F_Quad) * cmd->getQuadCount()); const Mat4& modelView = cmd->getModelView(); for(ssize_t i=; i< cmd->getQuadCount() * ; ++i)
{
V3F_C4B_T2F *q = &_quadVerts[i + _numberQuads * ];
Vec3 *vec1 = (Vec3*)&q->vertices;
modelView.transformPoint(vec1);
} _numberQuads += cmd->getQuadCount();
}
 
1》首先, Render在初始化时, 初始化了一个的很大的顶点信息的缓存区, 我们的QuadCommand中保存的节点信息, 会先被复制到这个缓存区中。 

memcpy(_quadVerts + _numberQuads * , cmd->getQuads(), sizeof(V3F_C4B_T2F_Quad) * cmd->getQuadCount());
2》对当前的顶点坐标信息, 做顶点坐标变化, 也就是模型坐标到世界坐标的变换
for(ssize_t i=; i< cmd->getQuadCount() * ; ++i)
{
V3F_C4B_T2F *q = &_quadVerts[i + _numberQuads * ];
Vec3 *vec1 = (Vec3*)&q->vertices;
modelView.transformPoint(vec1);
}
3 记录当前Render中有多少个这种矩形信息。 

_numberQuads += cmd->getQuadCount();
 
 
3.3 绘制
 
flush() 关于这块的OpenGL相关的函数,(VAO, VBO,glDrawBuffer) 参考下面的附录:
 
1 绑定VAO, 这里的VAO在 setupVBOAndVAO里已经初始化过了。初始化时,对顶点的索引值进行了初始化。 

  GL::bindVAO(_quadVAO);
2 设置VBO数据 

//Set VBO data
glBindBuffer(GL_ARRAY_BUFFER, _quadbuffersVBO[]);
// option 3: orphaning + glMapBuffer
glBufferData(GL_ARRAY_BUFFER, sizeof(_quadVerts[]) * _numberQuads * , nullptr, GL_DYNAMIC_DRAW);
void *buf = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
memcpy(buf, _quadVerts, sizeof(_quadVerts[])* _numberQuads * );
glUnmapBuffer(GL_ARRAY_BUFFER);
       
3 绑定顶点索引数据 , 这里的索引数据, 详细看 setupVBOAndVAO()
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _quadbuffersVBO[]);
 
4 遍历之前的批绘制列表,(这个有一个优化,就是当相邻的矩形的材质相同时,统一在一个绘制命令中绘制)

 for(const auto& cmd : _batchQuadCommands)
5 绘制

 glDrawElements(GL_TRIANGLES, (GLsizei) indexToDraw, GL_UNSIGNED_SHORT, (GLvoid*) (startIndex*sizeof(_indices[])) );
 
 
源码:
 
void Renderer::drawBatchedQuads()
{
//TODO: we can improve the draw performance by insert material switching command before hand. int indexToDraw = ;
int startIndex = ; //Upload buffer to VBO
if(_numberQuads <= || _batchQuadCommands.empty())
{
return;
} if (Configuration::getInstance()->supportsShareableVAO())
{
//Bind VAO
GL::bindVAO(_quadVAO);
//Set VBO data
glBindBuffer(GL_ARRAY_BUFFER, _quadbuffersVBO[]);
// option 3: orphaning + glMapBuffer
glBufferData(GL_ARRAY_BUFFER, sizeof(_quadVerts[]) * _numberQuads * , nullptr, GL_DYNAMIC_DRAW);
void *buf = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
memcpy(buf, _quadVerts, sizeof(_quadVerts[])* _numberQuads * );
glUnmapBuffer(GL_ARRAY_BUFFER);
glBindBuffer(GL_ARRAY_BUFFER, );
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _quadbuffersVBO[]);
}
else
{
}
//Start drawing verties in batch
for(const auto& cmd : _batchQuadCommands)
{
auto newMaterialID = cmd->getMaterialID();
if(_lastMaterialID != newMaterialID || newMaterialID == MATERIAL_ID_DO_NOT_BATCH)
{
//Draw quads
if(indexToDraw > )
{
glDrawElements(GL_TRIANGLES, (GLsizei) indexToDraw, GL_UNSIGNED_SHORT, (GLvoid*) (startIndex*sizeof(_indices[])) );
_drawnBatches++;
_drawnVertices += indexToDraw;
startIndex += indexToDraw;
indexToDraw = ;
}
//Use new material
cmd->useMaterial();
_lastMaterialID = newMaterialID;
} indexToDraw += cmd->getQuadCount() * ;
} //Draw any remaining quad
if(indexToDraw > )
{
glDrawElements(GL_TRIANGLES, (GLsizei) indexToDraw, GL_UNSIGNED_SHORT, (GLvoid*) (startIndex*sizeof(_indices[])) );
_drawnBatches++;
_drawnVertices += indexToDraw;
} if (Configuration::getInstance()->supportsShareableVAO())
{
//Unbind VAO
GL::bindVAO();
}
else
{
glBindBuffer(GL_ARRAY_BUFFER, );
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, );
} _batchQuadCommands.clear();
_numberQuads = ;
}
 
参考:
VAO&VBO 
OpenGL ES 2.0 
cocos2D-X源码分析之从cocos2D-X学习OpenGL(2)----QUAD_COMMAND
 

5 cocos2dx 3.0源码分析 渲染 render的更多相关文章

  1. 3 cocos2dx 3.0 源码分析-mainLoop详细

    简述:   我靠上面图是不是太大了, 有点看不清了.  总结一下过程: 之前说过的appController 之后经过了若干初始化, 最后调用了displayLinker 的定时调用, 这里调用了函数 ...

  2. 4 cocos2dx 3.0 源码分析- scheduler

    scheduler 这个类, 负责了引擎的自定义更新, 及定时更新相关的操作, 看看下面的代码,很熟悉吧.   schedule(schedule_selector(HelloWorld::updat ...

  3. 2 cocos2dx 3.0 源码分析-Director

    Director 导演类, 这个类在整个引擎中担当着最重要的角色, 先看看它是如何初始化的,它共管理了哪些内容呢?    1初始化- 更新处理Scheduler   Scheduler 这个类负责用户 ...

  4. Yii2.0源码分析之——控制器文件分析(Controller.php)创建动作、执行动作

    在Yii中,当请求一个Url的时候,首先在application中获取request信息,然后由request通过urlManager解析出route,再在Module中根据route来创建contr ...

  5. AFNetWorking3.0源码分析

    分析: AFNetWorking(3.0)源码分析(一)——基本框架 AFNetworking源码解析 AFNetworking2.0源码解析<一> end

  6. Solr5.0源码分析-SolrDispatchFilter

    年初,公司开发法律行业的搜索引擎.当时,我作为整个系统的核心成员,选择solr,并在solr根据我们的要求做了相应的二次开发.但是,对solr的还没有进行认真仔细的研究.最近,事情比较清闲,翻翻sol ...

  7. Solr4.8.0源码分析(25)之SolrCloud的Split流程

    Solr4.8.0源码分析(25)之SolrCloud的Split流程(一) 题记:昨天有位网友问我SolrCloud的split的机制是如何的,这个还真不知道,所以今天抽空去看了Split的原理,大 ...

  8. Solr4.8.0源码分析(24)之SolrCloud的Recovery策略(五)

    Solr4.8.0源码分析(24)之SolrCloud的Recovery策略(五) 题记:关于SolrCloud的Recovery策略已经写了四篇了,这篇应该是系统介绍Recovery策略的最后一篇了 ...

  9. Solr4.8.0源码分析(23)之SolrCloud的Recovery策略(四)

    Solr4.8.0源码分析(23)之SolrCloud的Recovery策略(四) 题记:本来计划的SolrCloud的Recovery策略的文章是3篇的,但是没想到Recovery的内容蛮多的,前面 ...

随机推荐

  1. AC日记——[USACO5.4]奶牛的电信Telecowmunication 洛谷 P1345

    [USACO5.4]奶牛的电信Telecowmunication 思路: 水题: 代码: #include <cstdio> #include <cstring> #inclu ...

  2. 深度扫盲JavaScript的模块化(AMD , CMD , CommonJs 和 ES6)

    原文地址 https://blog.csdn.net/haochangdi123/article/details/80408874 一.commonJS 1.内存情况 对于基本数据类型,属于复制.即会 ...

  3. [I/O]javaI/O工作机制

    摘要:IO问题可以说是当今web应用中面临的主要问题之一.因为在这个数据爆发的时代,海量的数据在网络到处流动,而在这个过程中都会涉及IO问题,可以说IO问题已经成为web应用的瓶颈之一.如何优化?以此 ...

  4. JAVA解析xml的四种方式比较

    1)DOM解析 DOM是html和xml的应用程序接口(API),以层次结构(类似于树型)来组织节点和信息片段,映射XML文档的结构,允许获取 和操作文档的任意部分,是W3C的官方标准 [优点] ①允 ...

  5. AOP的工作模式

    代理主要有静态代理和动态代理. 静态代理:在代理中实现接口并创建实现类对象,在对实现类的方法增加功能(不常用). 动态代理:实现implements InvocationHandler接口.实现方法: ...

  6. iOS 9音频应用播放音频之控制播放速度

    iOS 9音频应用播放音频之控制播放速度 iOS 9音频控制播放速度 iOS9音频文件在播放时是以一定的速度进行的.这个速度是可以进行更改的,从而实现iOS9音频文件的快速播放和慢速播放功能.要实现i ...

  7. WSDL语法

    <什么是WSDL语言> WSDL(网络服务描述语言,Web Services Description Language)是一门基于 XML 的语言,用于描述 Web Services 以及 ...

  8. 【BZOJ 4070】【APIO 2015】雅加达的摩天楼

    http://www.lydsy.com/JudgeOnline/problem.php?id=4070 分块建图. 对每个\(P_i\)分类讨论,\(P_i>\sqrt N\)则直接连边,边数 ...

  9. codeforces 220 C. Game on Tree

    题目链接 codeforces 220 C. Game on Tree 题解 对于 1节点一定要选的 发现对于每个节点,被覆盖切选中其节点的概率为祖先个数分之一,也就是深度分之一 代码 #includ ...

  10. 主席树+dfs SPOJ BZOJ2588 Count on a tree

    这道题我由于智障错误导致一直错. 在树上建主席树,加上lca思想,很简单. #include<bits/stdc++.h> using namespace std; ; struct no ...