ibcadmin 发表于 2019-10-24 09:51:04

Cocos2d-x 学习笔记(25) 渲染 绘制 Render

<blockquote>
<pstyle="text-align: center;"><strong>【Cocos2d-x】学习笔记目次</strong></p>
<p style="text-align: center;">本文链接:https://www.cnblogs.com/deepcho/p/cocos2dx-render.html<br /></p>

</blockquote>
<h2>1. 从步调入口到渲染方法</h2>
<p>一个Cocos2d-x项目流程中,在每一帧举行一次渲染,渲染的时机是在调度器update方法实行之后。所渲染的是当前的场景_runningScene,当前场景实行Scene::render()方法举行渲染。</p>
<p>在场景的渲染方法Scene::render()中,对UI树举行中序遍历,遍历到的元素实行其draw方法。对于Sprite,其draw方法是重要语句:</p>

      _trianglesCommand.init(_globalZOrder,
                               _texture,
                               getGLProgramState(),
                               _blendFunc,
                               _polyInfo.triangles,
                               transform,
                               flags);

      renderer->addCommand(&_trianglesCommand);

<p>Sprite有成员变量TrianglesCommand _trianglesCommand,这是该Sprite的渲染命令。渲染命令在初始化后被添加到对应的渲染队列中。末了实行Renderer::render()方法。</p>
<p>从步调入口到Renderer::render()方法与渲染相干流程如下:</p>
<p><div align="center"></div></p>
<p> </p>
<p> </p>
<h2>2. Renderer::render()</h2>
<p>Node的draw方法只是将该Node的渲染命令加入到队列中,渲染的实行由Renderer::render()方法举行。</p>
<p>Renderer类有两个重要成员变量,也是两个容器:</p>

std::stack<int> _commandGroupStack;
std::vector<RenderQueue> _renderGroups;

<p>_commandGroupStack是存储ID的栈。</p>
<p>_renderGroups是存储RenderQueue的容器,RenderQueue类实质是一个存储了5种渲染命令的容器:</p>

std::vector<RenderCommand*> _commands;

<p>该容器是渲染命令的队列,队列里的命令分为5类分别存储,每类代表不同的含义:</p>

    enum QUEUE_GROUP
    {
      /**Objects with globalZ smaller than 0.*/
      GLOBALZ_NEG = 0,
      /**Opaque 3D objects with 0 globalZ.*/
      OPAQUE_3D = 1,
      /**Transparent 3D objects with 0 globalZ.*/
      TRANSPARENT_3D = 2,
      /**2D objects with 0 globalZ.*/
      GLOBALZ_ZERO = 3,
      /**Objects with globalZ bigger than 0.*/
      GLOBALZ_POS = 4,
      QUEUE_COUNT = 5,
    };

<p>Renderer::render()方法的实行必要渲染命令中的一些数据,对于Sprite,它的渲染命令是TrianglesCommand。</p>
<p>RenderCommand是TrianglesCommand的父类。RenderCommand有成员变量枚举Type,其界说如下:</p>

    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
    };

<p>TrianglesCommand的Type值是TRIANGLES_COMMAND。</p>
<p>在Sprite的draw方法中,TrianglesCommand范例的渲染命令通过init方法初始化。</p>
<p>该init方法首先调用父类RenderCommand的init方法设置3个变量:globalOrder, mv, flags。</p>
<p>之后将参数triangles赋给成员_triangles,这是一个结构体,其构成如下:</p>

    struct Triangles
    {
      /**Vertex data pointer.*/
      V3F_C4B_T2F* verts;
      /**Index data pointer.*/
      unsigned short* indices;
      /**The number of vertices.*/
      int vertCount;
      /**The number of indices.*/
      int indexCount;
    };

<p>包罗全部顶点数据容器、索引数据容器、顶点个数、索引个数。顶点数据利用V3F_C4B_T2F结构体存储。</p>
<p>接下来设置矩阵_mv、_textureID 、_blendType、_glProgramState。这里的_textureID是由参数纹理实行getName方法得到的。</p>
<p>init方法末了实行generateMaterialID()方法天生材质ID。该方法是通过四个变量_textureID、_blendType.src、_blendType.dst、_glProgramState,计算哈希值作为材质ID(变量_materialID)。也就是说,当两个渲染命令的四个变量完全划一时,两个渲染命令(两个Sprite)的材质才算是雷同的。</p>
<p>Renderer::render()内的实行流程大致如下:</p>
<p><div align="center"></div></p>
<p>Renderer::render()方法对_renderGroups里的每个渲染队列实行sort方法排序,对每个队列中的TRANSPARENT_3D范例的渲染命令按Depth从小到大举行排序,对GZOrder小于0的渲染命令、GZOrder大于0的渲染命令按ZOrder从小到大举行排序。此时没有对GZOrder等于0的渲染命令排序,因为这些渲染命令的添加是按照所属的Node的LocalZOrder序次添加的,即已经排好序,无需再次排序。</p>
<p>排序后实行visitRenderQueue(_renderGroups),该方法是按队列里命令分类的序次,依次对每个分类的每个命令实行processRenderCommand方法。</p>
<p>processRenderCommand方法里会对参数命令的Type举行判断。对于Sprite的TrianglesCommand命令,当VBO的buffer已满时,会触发drawBatchedTriangles方法;当没满时,命令会存入到Renderer容器vector<TrianglesCommand*> _queuedTriangleCommands中。</p>
<p>刚才讲到visitRenderQueue()方法对队列里的每个命令实行processRenderCommand()方法,重要是遍历队列内的每个分类,把命令加到容器中。在当前分类的命令都被遍历之后,实行flush()方法,该方法重要是调用了drawBatchedTriangles()方法。</p>
<p>drawBatchedTriangles()方法对存储命令的容器举行遍历,对每个命令的操纵可分为四个步调:</p>
<p><div align="center"></div></p>
<p> </p>
<p> </p>
<h3>装载</h3>
<p>每个命令实行fillVerticesAndIndices方法,添补Renderer的顶点容器_verts和索引容器_indices,详细做法是:将命令的顶点坐标转为该顶点的世界坐标,再存入到_verts中,再将命令的索引存入到_indices中,末了修改(增长)_filledVertex和_filledIndex的值。</p>
<p>接下来,判断批量渲染的条件是否成立,重要是比力当前命令材质ID和上个命令材质ID。如果可以举行批量绘制,把当前命令的信息加入到容器数组_triBatchesToDraw[]中,下标为前次操纵的容器下标。该容器大致先容如下:</p>

TriBatchToDraw* _triBatchesToDraw;

// Internal structure that has the information for the batches
struct TriBatchToDraw {
    TrianglesCommand* cmd;// needed for the Material
    GLsizei indicesToDraw;
    GLsizei offset;
};

<p>如果该命令不能举行批量绘制,则让容器数组下标加1,新容器存储当前命令的信息。</p>
<h3>顶点和索引复制到GL缓存</h3>
<p>很简朴。_verts和_indices内的数据被复制到GL对象的缓存里。</p>
<h3>绘制</h3>
<p>绘制的参数是装载步调时的TriBatchToDraw内的信息。每个TriBatchToDraw举行一次绘制,也导致了每次绘制时DrawCall的值加1。</p>
<h3>清算</h3>
<p>很简朴,就不先容了。</p>
<p>以上就是渲染的全流程剖析。可以总结出,渲染是在每帧结束进步行的;渲染之前是把每帧的全部元素的绘制用命令统一举行存储,在渲染时读取这些命令,举行绘制;渲染时还会举行批量绘制的判断,这能有用低落DrawCall值。</p>
<p>有关低落DrawCall值的学习在这篇文章里:Cocos2d-x 学习笔记(26) 从源码学习 DrawCall 的低落方法</p>
<hr />
<p></p>
<p><em>本文链接:https://www.cnblogs.com/deepcho/p/cocos2dx-render.html<br /></em></p><br><br/><br/><br/><br/><br/>来源:<a href="https://www.cnblogs.com/deepcho/p/cocos2dx-render.html" target="_blank">https://www.cnblogs.com/deepcho/p/cocos2dx-render.html</a>
页: [1]
查看完整版本: Cocos2d-x 学习笔记(25) 渲染 绘制 Render