缩略渲染过程
1、
DisplayLinkDirector
::mainLoop()
{ drawScene }
2、
|
程序的主循环
|
-->DisplayLinkDirector::drawScene() { _runningScene->visit _renderer->render();
}
3、
|
在drawScene里面的一句代码_runningScene->visit
|
-->visit(_renderer,Mat4::IDENTITY,false); { if(!_children.empty()) { sortAllChildren(); this->draw } else { this->draw}
}
4、
|
递归visit |
Sprite::draw() { 产生渲染命令
}
5、
|
产生渲染命令
|
-->voidRenderer::visitRenderQueue(constRenderQueue&queue) { if(RenderCommand::Type::QUAD_COMMAND) elseif(RenderCommand::Type::GROUP_COMMAND) elseif(RenderCommand::Type::CUSTOM_COMMAND) elseif(RenderCommand::Type::BATCH_COMMAND==commandType) elseif(RenderCommand::Type::MESH_COMMAND==commandType) else
}
|
在drawScene里面的一句代码_renderer->render(); 排序渲染队列执行opengl命令 |
void
DisplayLinkDirector
::mainLoop()
{
if
(_purgeDirectorInNextLoop)
{
_purgeDirectorInNextLoop =
false
;
purgeDirector();
}
else
if
(! _invalid)
{
drawScene();
// release the objects
PoolManager
::getInstance()->getCurrentPool()->clear();
}
}
上面是主循环函数 有个drawScene 跟踪进去
// draw the scene
if
(_runningScene)
{
_runningScene->visit(_renderer,
Mat4
::IDENTITY,
false
);
_eventDispatcher->dispatchEvent(_eventAfterVisit);//事件分发 在每一帧的绘制里面
}
_renderer->render();
上面的visit是产生渲染队列的,上面有个render跟踪进去
void
Renderer
::render()
{
//Uncomment this once everything is rendered by new renderer
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//TODO setup camera or MVP
_isRendering =
true
;
if
(_glViewAssigned)
{
// cleanup
_drawnBatches = _drawnVertices = 0;
//Process render commands
//1. Sort render commands based on ID
for
(
auto
&renderqueue : _renderGroups)
{
renderqueue.sort();
}
visitRenderQueue(_renderGroups[0]);
flush();
}
clean();
_isRendering =
false
;
}
渲染队列排序 之后到函数
void
Renderer
::visitRenderQueue(
const
RenderQueue
&
queue
)
{
ssize_t
size =
queue
.size();
for
(
ssize_t
index = 0; index < size; ++index)
{
auto
command =
queue
[index];
auto
commandType = command->getType();
if
(
RenderCommand
::
Type
::
QUAD_COMMAND
== commandType)
{
auto
cmd =
static_cast
<
QuadCommand
*>(command);
//Batch quads
if
(_numQuads + cmd->getQuadCount() > VBO_SIZE)
{
CCASSERT
(cmd->getQuadCount()>= 0 && cmd->getQuadCount() < VBO_SIZE,
"VBO is not big enough for quad data,please break the quad data down or use customized render command"
);
//Draw batched quads if VBO is full
drawBatchedQuads();
}
_batchedQuadCommands.push_back(cmd);
memcpy(_quads + _numQuads,cmd->getQuads(),
sizeof
(
V3F_C4B_T2F_Quad
) * cmd->getQuadCount());
convertToWorldCoordinates(_quads + _numQuads,cmd->getQuadCount(),cmd->getModelView());
_numQuads += cmd->getQuadCount();
}
else
if
(
RenderCommand
::
Type
::
GROUP_COMMAND
== commandType)
{
flush();
int
renderQueueID = ((
GroupCommand
*) command)->getRenderQueueID();
visitRenderQueue(_renderGroups[renderQueueID]);
}
else
if
(
RenderCommand
::
Type
::
CUSTOM_COMMAND
== commandType)
{
flush();
auto
cmd =
static_cast
<
CustomCommand
*>(command);
cmd->execute();
}
else
if
(
RenderCommand
::
Type
::
BATCH_COMMAND
== commandType)
{
flush();
auto
cmd =
static_cast
<
BatchCommand
*>(command);
cmd->execute();
}
else
if
(
RenderCommand
::
Type
::
MESH_COMMAND
== commandType)
{
flush();
auto
cmd =
static_cast
<
MeshCommand
*>(command);
cmd->execute();
}
else
{
CCLOGERROR
(
"Unknown commands in renderQueue"
);
}
}
}
3.0之前是visit后就draw,而draw是真正的OpenGL操作。也就是说,每访问一个对象,先计算节点的渲染数据,然后马上渲染。
3.0也是在visit后就draw,但是draw并不进行OpenGL操作。3.0抽象了一个RenderCommand,在draw的时候其实是生成一个渲染命令,渲染命令其实就是对渲染所需要的数据的封装。RenderCommand作为基类,只包含了两个成员,一个是命令类型_type,这个很必要,正是靠这个来获取子类对象的具体类型的,这里没有用运行时类型,应该是考虑到效率;另一个是z深度_globalOrder,这个也很必要,渲染的时候必然要对节点排序,而z序是唯一的依据。
3.0包含了以下几个RenderCommand的子类:
1,CustomCommand:
顾名思义,是客户自定义的。
使用CustomCommand还有Layer,LableAtlas,LabelBMFont
2,QuadCommand:
和CustomCommand不同,这个类包含了很多属性:纹理_textureID,Program _shader,混合函数_blendType,定点数据_quad,模型视图矩阵_mv,还有一个材质属性_materialID。。
值得一提的是3.0主要是对QuadCommand进行了优化,实际上游戏中也大多数是这种情况。在Render::render()函数中,如果当前是QuadCommand,则顶点和Command放入数组,如果是其它,则先渲染此前的QuadCommand数组,然后再渲染当前命令。
使用CustomCommand命令的类有Sprite,ParticleSystemQuad,而Sprite应该是游戏中最常用的类,所以3.0的优化还是很有意义的。
3,BatchCommand
看名字就知道,BatchCommand是CCSpriteBatchNode的改进,而事实上,也的确是CCSpriteBatchNode和CCPaticleBatchNode使用了BatchCommand。BatchCommand的属性和QuadCommand很相似,不过没有了顶点数据_quad,变成了_textureAtlas,其渲染函数也和此前的差别不大,都是材质三剑客(shader,texutre,blend)
4,GroupCommand:
这是一个Command组合,但是GroupCommand只有一个属性_renderQueueID,而GroupCommand的所有子Command其实存放在Render中。GroupCommand像其它Command一样,存放在同一个队列中,遍历的时候,如果是GroupCommand,则获取其_renderQueueID,然后根据_renderQueueID找到指定的RenderCommand队列,进而渲染这个队列。可见,在Render中,可以包含多个Command队列,一些是根节点队列(没有父Command),其它的是指定ID的GroupCommand的子命令队列。
使用GroupCommand的主要有RenderTexture,因为RenderTexture包含了两次分隔的渲染操作,一次是begin(),一次是end()。
命令从哪里来的呢????
这个是刚刚的visit函数:
void
Node
::visit(
Renderer
*
renderer
,
const
Mat4
&
parentTransform
,
bool
parentTransformUpdated
)
{
// quick return if not visible. children won't be drawn.
if
(!_visible)
{
return
;
}
bool
dirty = _transformUpdated ||
parentTransformUpdated
;
if
(dirty)
_modelViewTransform =
this
->transform(
parentTransform
);
_transformUpdated =
false
;
// IMPORTANT:
// To ease the migration to v3.0,we still support the Mat4 stack,
// but it is deprecated and your code should not rely on it
Director
* director =
Director
::getInstance();
CCASSERT
(
nullptr
!= director,
"Director is null when seting matrix stack"
);
director->pushMatrix(
MATRIX_STACK_TYPE
::
MATRIX_STACK_MODELVIEW
);
director->loadMatrix(
MATRIX_STACK_TYPE
::
MATRIX_STACK_MODELVIEW
,_modelViewTransform);
int
i = 0;
if
(!_children.empty())
{
sortAllChildren();
// draw children zOrder < 0
for
( ; i < _children.size(); i++ )
{
auto
node = _children.at(i);
if
( node && node->_localZOrder < 0 )
node->visit(
renderer
,_modelViewTransform,dirty);
else
break
;
}
// self draw
this
->draw(
renderer
,dirty);
for
(
auto
it=_children.cbegin()+i; it != _children.cend(); ++it)
(*it)->visit(
renderer
,dirty);
}
else
{
this
->draw(
renderer
,dirty);
}
// reset for next frame
_orderOfArrival = 0;
director->popMatrix(
MATRIX_STACK_TYPE
::
MATRIX_STACK_MODELVIEW
);
}
看到源码如果没有儿子了 那么调用draw Node:draw是空的 因为node不画 画的是sprite这些
void
Sprite
::draw(
Renderer
*
renderer
,
const
Mat4
&
transform
,
bool
transformUpdated
)
{
// Don't do calculate the culling if the transform was not updated
_insideBounds =
transformUpdated
?
renderer
->checkVisibility(
transform
,_contentSize) : _insideBounds;
if
(_insideBounds)
{
_quadCommand.init(_globalZOrder,_texture->getName(),getGLProgramState(),_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
//CC_SPRITE_DEBUG_DRAW
}
}