前面文章分析过一些cocos2d中2D的渲染流程,本文再详细补充有关cocos2d中Renderer渲染类的相关细节。
Renderer中的渲染队列
Renderer对象在Director(导演类)中,Director一般是单例,因此可以认为Renderer也是全局唯一。Renderer里面存放着渲染命令队列,它的类的简化结构如下:

再画个详细点的渲染命令队列图:

从图中可以看到,RenderQueue中定义了5种类型的渲染命令,如下代码:
class RenderQueue {
public:
/**
RenderCommand will be divided into Queue Groups.
*/
enum QUEUE_GROUP
{
/**Objects with globalZ smaller than 0.*/
GLOBALZ_NEG = 0, //globalZ值小于0的物体
/**Opaque 3D objects with 0 globalZ.*/
OPAQUE_3D = 1, //不透明的3D物体,globalZ都是0
/**Transparent 3D objects with 0 globalZ.*/
TRANSPARENT_3D = 2, //透明的3D物体,globalZ都是0
/**2D objects with 0 globalZ.*/
GLOBALZ_ZERO = 3, //globalZ为0的2D物体
/**Objects with globalZ bigger than 0.*/
GLOBALZ_POS = 4, //globalZ大于0的物体
QUEUE_COUNT = 5,
};
//省略...
}
globalZ对应RenderCommand对象中的_globalOrder成员,渲染的顺序是_globalOrder小的先渲染,我个人的理解是,_globalOrder与我们平时理解的OpenGL中的z缓冲中的深度还是有区别的,对于2D物体而言,因为2D没有z轴,因此_globalOrder用来定义2D图像是否需要绘制,或者在绘制多个2D图像时,由谁覆盖谁。对于3D物体而言,3D物体的渲染顺序由RenderCommand的另一个成员_depth决定,_globalOrder恒为0。
渲染流程
回顾前面文章《cocos2d源码分析(四):渲染流程》中,cocos2d渲染放在主线程,流程如下:

我们从Renderer::render()开始继续分析,大致流程如下:

我们再详细分析下源码,Renderer::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)
{
//Process render commands
//1. Sort render commands based on ID
for (auto &renderqueue : _renderGroups)
{
renderqueue.sort(); //对renderqueue中的命令进行排序
}
visitRenderQueue(_renderGroups[0]); //执行渲染
}
clean(); //渲染完成后,清空_renderGroups中的所有command
_isRendering = false;
}
其中renderqueue.sort()的函数如下:
void RenderQueue::sort()
{
// Don't sort _queue0, it already comes sorted
std::stable_sort(std::begin(_commands[QUEUE_GROUP::TRANSPARENT_3D]), std::end(_commands[QUEUE_GROUP::TRANSPARENT_3D]), compare3DCommand);
std::stable_sort(std::begin(_commands[QUEUE_GROUP::GLOBALZ_NEG]), std::end(_commands[QUEUE_GROUP::GLOBALZ_NEG]), compareRenderCommand);
std::stable_sort(std::begin(_commands[QUEUE_GROUP::GLOBALZ_POS]), std::end(_commands[QUEUE_GROUP::GLOBALZ_POS]), compareRenderCommand);
}
我们可以看到,RenderQueue::sort()中只对TRANSPARENT_3D,GLOBALZ_NEG和GLOBALZ_POS三种类型的队列进行了排序,并且使用不同的比较函数,compare3DCommand函数是比较两个command的_depth值,如下:
static bool compare3DCommand(RenderCommand* a, RenderCommand* b)
{
return a->getDepth() > b->getDepth();
}
compareRenderCommand函数比较的是两个command的_globalOrder值。如下:
static bool compareRenderCommand(RenderCommand* a, RenderCommand* b)
{
return a->getGlobalOrder() < b->getGlobalOrder();
}
排序后,接着调用visitRenderQueue函数,因为代码很长,做下简化如下
void Renderer::visitRenderQueue(RenderQueue& queue)
{
queue.saveRenderState(); //保存当前的OpenGL状态
//
//判断5种类型的队列是否存在,然后调processRenderCommand
//如渲染不透明的3D物体
const auto& opaqueQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::OPAQUE_3D);
if (opaqueQueue.size() > 0)
{
//Clear depth to achieve layered rendering
glEnable(GL_DEPTH_TEST); //开启深度测试
glDepthMask(true);
glDisable(GL_BLEND); //关闭混合
glEnable(GL_CULL_FACE); //开启背面裁剪
RenderState::StateBlock::_defaultState->setDepthTest(true);
RenderState::StateBlock::_defaultState->setDepthWrite(true);
RenderState::StateBlock::_defaultState->setBlend(false);
RenderState::StateBlock::_defaultState->setCullFace(true);
for (const auto& opaqueNext : opaqueQueue)
{
processRenderCommand(opaqueNext);
}
flush();
}
//
queue.restoreRenderState(); //恢复OpenGL状态
}
在visitRenderQueue函数中,因为在处理队列的过程中,可能会改变OpenGL当前的状态,因此,首先调用了queue.saveRenderState(); 它是为了保存当前的OpenGL状态,在完成队列的渲染后,再调用queue.restoreRenderState();来恢复OpenGL状态。saveRenderState函数如下:
void RenderQueue::saveRenderState()
{
_isDepthEnabled = glIsEnabled(GL_DEPTH_TEST) != GL_FALSE; //当前是否开启了深度测试
_isCullEnabled = glIsEnabled(GL_CULL_FACE) != GL_FALSE; //当前是否开启了背面裁剪
glGetBooleanv(GL_DEPTH_WRITEMASK, &_isDepthWrite); //当前深度缓冲区是否可写
CHECK_GL_ERROR_DEBUG();
}
restoreRenderState函数如下:
void RenderQueue::restoreRenderState()
{
if (_isCullEnabled) //恢复之前的OpenGL状态
{
glEnable(GL_CULL_FACE);
RenderState::StateBlock::_defaultState->setCullFace(true);
}
else
{
glDisable(GL_CULL_FACE);
RenderState::StateBlock::_defaultState->setCullFace(false);
}
if (_isDepthEnabled)
{
glEnable(GL_DEPTH_TEST);
RenderState::StateBlock::_defaultState->setDepthTest(true);
}
else
{
glDisable(GL_DEPTH_TEST);
RenderState::StateBlock::_defaultState->setDepthTest(false);
}
glDepthMask(_isDepthWrite);
RenderState::StateBlock::_defaultState->setDepthWrite(_isDepthEnabled);
CHECK_GL_ERROR_DEBUG();
}
最后,processRenderCommand函数会根据不同的命令类型进行相应的渲染,在下一篇分析。
网友评论