渲染队列RenderQueue
RenderQueue
遍历场景时会生成渲染命令,生成的渲染命令存储在渲染队列中。渲染队列RenderQueue会将命令分层5类存储,分别是:
class RenderQueue {
public:
/**
RenderCommand will be divided into Queue Groups.
渲染队列RenderQueue把渲染命令分成五种存储
*/
enum QUEUE_GROUP
{
/**Objects with globalZ smaller than 0. globalZ< 0的命令*/
GLOBALZ_NEG = 0,
/**Opaque 3D objects with 0 globalZ. globalZ=0的不透明3D命令*/
OPAQUE_3D = 1,
/**Transparent 3D objects with 0 globalZ. globalZ=0的3D透明命令*/
TRANSPARENT_3D = 2,
/**2D objects with 0 globalZ. globalZ=0的2D命令*/
GLOBALZ_ZERO = 3,
/**Objects with globalZ bigger than 0.globalZ>0的命令 */
GLOBALZ_POS = 4,
QUEUE_COUNT = 5,
};
std::vector<RenderCommand*> _commands[QUEUE_COUNT];
}
其中的globalZ对应节点的_globalZOrder。globalZ=0的命令分成了三种首先按3D、2D进行分类,然后3D还分成是否透明。这样分类的原因是:2D、3D在显示画面先后顺序时,2D一般由绘制的先后顺序决定,而3D则会使用深度缓存;3D 分成是否透明是因为透明物体显示跟绘制命令的先后顺序有关(具体参考《OpenGL编程指南(第八版)》11.4.1顺序无关透明),所以3D透明队列再绘制前需要按照绘制物体的深度进行排序。
命令排序:GLOBALZ_NEG、GLOBALZ_POS、TRANSPARENT_3D命令在绘制前会进行排序,GLOBALZ_NEG、GLOBALZ_POS会根据globalZ的值从小到大排序,TRANSPARENT_3D会根据绘制物体的深度(离摄像机的远近)进行排序。
Renderer类种并不是只有一个RenderQueue,而有一个RenderQueue的数组_renderGroups,之所以这样是因为渲染有时某些命令需要单独设置一些状态,例如使用模板缓存实现“镂空”绘制等,这时就需要一个单独的RenderQueue去创建一个渲染分支实现,以防止影响全局绘制。
class CC_DLL Renderer
{
std::vector<RenderQueue> _renderGroups;
}
渲染都是从_renderGroups[0]开始绘制,如果不使用渲染分支,也就只会用到_renderGroups[0]一个RenderQueue。如果想使用其他的RenderQueue创建渲染分支,需要使用GroupCommand渲染命令。
处理渲染命令
处理步骤如下:
对_renderGroups的每个RenderQueue中的命令进行排序
使用函数visitRenderQueue访问_renderGroups[0]中的命令,访问顺序为GLOBALZ_NEG、OPAQUE_3D、TRANSPARENT_3D、GLOBALZ_ZERO、GLOBALZ_POS。访问每个渲染命令数组访问时会先设置好OpenGL的状态,然后再用函数processRenderCommand处理渲染命令。
processRenderCommand处理渲染,按渲染命令类型对么每个命令进行渲染
渲染命令
一共有6种渲染命令,每种命令类型对应一个类,这些类都继承至RenderCommand类,对应关系以及作用如下:
TRIANGLES_COMMAND:TrianglesCommand,渲染三角形,可以合并命令减少OpenGL的调用提高渲染效率
MESH_COMMAND:MeshCommand,渲染3D
GROUP_COMMAND:GroupCommand,创建渲染分支,使用_renderGroups[0]之外的RenderQueue
CUSTOM_COMMAND:CustomCommand,自定义渲染命令
BATCH_COMMAND:BatchCommand,同时渲染多个使用同一纹理的图形,提高渲染效率
PRIMITIVE_COMMAND:PrimitiveCommand,渲染自定义图元。
class CC_DLL RenderCommand
{
public:
/**Enum the type of render command. */
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 //可以合批渲染
};
}
接下来由易到难对每种命令单独讲解,并用Node类实现相应的命令,因为渲染跟OpenGL紧密相关,这一块需要OpenGL的知识才能完全理解。
CustomCommand
CustomCommand是所有命令中最简单的一个,也是最灵活的一个,绘制的内容和方式完全交由我们自己决定。LayerColor、DrawNode、Skybox等待都是使用CustomCommand命令进行绘制的。
CustomCommand有一个std::function<void()> func变量,当Renderer处理CustomCommand时只是简单的调用func函数,以下是Renderer中的代码:
void Renderer::processRenderCommand(RenderCommand* command)
{
else if(RenderCommand::Type::CUSTOM_COMMAND == commandType)
{
flush();
auto cmd = static_cast<CustomCommand*>(command);
CCGL_DEBUG_INSERT_EVENT_MARKER("RENDERER_CUSTOM_COMMAND");
cmd->execute();
}
}
void CustomCommand::execute()
{
if(func)
{
func();
}
}