从上一章cocos启动流程中知道,cocos的渲染队列中,总共有TRIANGLES_COMMAND, GROUP _COMMAND等七种,其中GROUP_COMMAND是命令集合,QUAD_COMMAND是TRIANGLES_COMMAND的扩展类,真正的渲染命令只有5种:CUSTOM_COMMAND用于自定义绘制,BATCH_COMMAND批次渲染图集,MESH_COMMAND网格渲染,主要用于渲染3D模型(Sprite3D),PRIMITIVE_ COMMAND用于点线等的绘制,TRIANGLES_COMMAND三角形绘制,主要绘制按钮,图片等矩形控件(sprite)。这次主要记录TRIANGLES_COMMAND的绘制。
游戏渲染消耗大的一个重要原因就是drawCall次数太多,drawCall用OPENGL描述就是调用绘制函数(drawElement等)的次数。cocos对TrianglesCommand的命令进行了优化,对使用同一材质的绘制命令中的顶点和索引进行合并,同一材质的命令进行批次渲染,我们项目的3D批次渲染也是这种思想。以sprite的绘制为例,每次渲染遍历场景时调用调用sprite的draw函数(流程见cocos启动流程),生成TrianglesCommand渲染命令,加入到Renderer的渲染队列:
TrianglesCommand _trianglesCommand;
void Sprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
_trianglesCommand.init(_globalZOrder,
_texture,
getGLProgramState(),
_blendFunc,
_polyInfo.triangles,
transform,
flags);
renderer->addCommand(&_trianglesCommand);
}
场景遍历完成后,Renderer访问渲染队列时,这里直接跳如处理流程(具体参见:cocos启动流程)如下:
void Renderer::processRenderCommand(RenderCommand* command)
{
auto commandType = command->getType();
if( RenderCommand::Type::TRIANGLES_COMMAND == commandType)
{
// flush other queues
flush3D();
auto cmd = static_cast<TrianglesCommand*>(command);
// queue it 将TrianglesCommand加入到队列
_queuedTriangleCommands.push_back(cmd);
_filledIndex += cmd->getIndexCount();
_filledVertex += cmd->getVertexCount();
}
。。。。。。。。
}
其他渲染命令,一般在此处绘制,TRIANGLES_COMMAND 渲染命令只是将所有渲染命令加入到统一队列,再访问过同一渲染类型(只是renderGroup一个渲染类型的集合,如透明和不透明)的所有渲染指令后,调用flush函数:
void Renderer::visitRenderQueue(RenderQueue& queue)
{
。。。。。
const auto& zZeroQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::GLOBALZ_ZERO);
if (zZeroQueue.size() > 0)
{
for (auto it = zZeroQueue.cbegin(); it != zZeroQueue.cend(); ++it)
{
processRenderCommand(*it);
}
flush();
}
。。。。。。。。
}
flush函数会调用flush2D(),这里只看flush2D,最终调用drawBatchedTriangles:
void Renderer::drawBatchedTriangles()
{
/*******1: Setup up vertices/indices 将所有的顶点和索引统一*************/
for(auto it ; it != std::end(_queuedTriangleCommands); ++it)
{
fillVerticesAndIndices(cmd);//集合顶点和索引到同一数组
// in the same batch ?是否在一个drawCall里渲染,根据材质ID和设置判断
if (batchable && (prevMaterialID == currentMaterialID || firstCommand))
{
_triBatchesToDraw[batchesTotal].indicesToDraw += cmd->getIndexCount();
_triBatchesToDraw[batchesTotal].cmd = cmd;
}
else
{
_triBatchesToDraw[batchesTotal].cmd = cmd;
_triBatchesToDraw[batchesTotal].indicesToDraw = (int) cmd->getIndexCount();
// is this a single batch ? Prevent creating a batch group then
}
}
/***** 2: Copy vertices/indices to GL objects将数组索引映射到渲染缓存*************/
glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]);
glBufferData(。。。。);
GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POS_COLOR_TEX);
//
/******3 根据合成的批次进行渲染 *************/
for (int i=0; i<batchesTotal; ++i)
glDrawElements(。。。。。) );
/******4: Cleanup解除数组和索引到缓存的绑定 *************/
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
_queuedTriangleCommands.clear();
}