1. 程式人生 > >‎Cocos2d-x 學習筆記(25) 渲染 繪製 Render

‎Cocos2d-x 學習筆記(25) 渲染 繪製 Render

【Cocos2d-x】學習筆記目錄

本文連結:https://www.cnblogs.com/deepcho/p/cocos2dx-render.html

1. 從程式入口到渲染方法

一個Cocos2d-x專案流程中,在每一幀進行一次渲染,渲染的時機是在排程器update方法執行之後。所渲染的是當前的場景_runningScene,當前場景執行Scene::render()方法進行渲染。

在場景的渲染方法Scene::render()中,對UI樹進行中序遍歷,遍歷到的元素執行其draw方法。對於Sprite,其draw方法是主要語句:

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

        renderer->addCommand(&_trianglesCommand);

Sprite有成員變數TrianglesCommand _trianglesCommand,這是該Sprite的渲染命令。渲染命令在初始化後被新增到對應的渲染佇列中。最後執行Renderer::render()方法。

從程式入口到Renderer::render()方法與渲染相關流程如下:

 

 

2. Renderer::render()

Node的draw方法只是將該Node的渲染命令加入到佇列中,渲染的執行由Renderer::render()方法進行。

Renderer類有兩個重要成員變數,也是兩個容器:

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

_commandGroupStack是儲存ID的棧。

_renderGroups是儲存RenderQueue的容器,RenderQueue類實質是一個儲存了5種渲染命令的容器:

std::vector<RenderCommand*> _commands[QUEUE_COUNT];

該容器是渲染命令的佇列,佇列裡的命令分為5類分別儲存,每類代表不同的含義:

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

Renderer::render()方法的執行需要渲染命令中的一些資料,對於Sprite,它的渲染命令是TrianglesCommand。

RenderCommand是TrianglesCommand的父類。RenderCommand有成員變數列舉Type,其定義如下:

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

TrianglesCommand的Type值是TRIANGLES_COMMAND。

在Sprite的draw方法中,TrianglesCommand型別的渲染命令通過init方法初始化。

該init方法首先呼叫父類RenderCommand的init方法設定3個變數:globalOrder, mv, flags。

之後將引數triangles賦給成員_triangles,這是一個結構體,其組成如下:

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

包括所有頂點資料容器、索引資料容器、頂點個數、索引個數。頂點資料使用V3F_C4B_T2F結構體儲存。

接下來設定矩陣_mv、_textureID 、_blendType、_glProgramState。這裡的_textureID是由引數紋理執行getName方法得到的。

init方法最後執行generateMaterialID()方法生成材質ID。該方法是通過四個變數_textureID、_blendType.src、_blendType.dst、_glProgramState,計算雜湊值作為材質ID(變數_materialID)。也就是說,當兩個渲染命令的四個變數完全一致時,兩個渲染命令(兩個Sprite)的材質才算是相同的。

Renderer::render()內的執行流程大致如下:

Renderer::render()方法對_renderGroups裡的每個渲染佇列執行sort方法排序,對每個佇列中的TRANSPARENT_3D型別的渲染命令按Depth從小到大進行排序,對GZOrder小於0的渲染命令、GZOrder大於0的渲染命令按ZOrder從小到大進行排序。此時沒有對GZOrder等於0的渲染命令排序,因為這些渲染命令的新增是按照所屬的Node的LocalZOrder順序新增的,即已經排好序,無需再次排序。

排序後執行visitRenderQueue(_renderGroups[0]),該方法是按佇列裡命令分類的順序,依次對每個分類的每個命令執行processRenderCommand方法。

processRenderCommand方法裡會對引數命令的Type進行判斷。對於Sprite的TrianglesCommand命令,當VBO的buffer已滿時,會觸發drawBatchedTriangles方法;當沒滿時,命令會存入到Renderer容器vector<TrianglesCommand*> _queuedTriangleCommands中。

剛才講到visitRenderQueue()方法對佇列裡的每個命令執行processRenderCommand()方法,主要是遍歷佇列內的每個分類,把命令加到容器中。在當前分類的命令都被遍歷之後,執行flush()方法,該方法主要是呼叫了drawBatchedTriangles()方法。

drawBatchedTriangles()方法對儲存命令的容器進行遍歷,對每個命令的操作可分為四個步驟:

 

 

裝載

每個命令執行fillVerticesAndIndices方法,填充Renderer的頂點容器_verts和索引容器_indices,具體做法是:將命令的頂點座標轉為該頂點的世界座標,再存入到_verts中,再將命令的索引存入到_indices中,最後修改(增加)_filledVertex和_filledIndex的值。

接下來,判斷批量渲染的條件是否成立,主要是比較當前命令材質ID和上個命令材質ID。如果可以進行批量繪製,把當前命令的資訊加入到容器陣列_triBatchesToDraw[]中,下標為上次操作的容器下標。該容器大致介紹如下:

TriBatchToDraw* _triBatchesToDraw;

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

如果該命令不能進行批量繪製,則讓容器陣列下標加1,新容器儲存當前命令的資訊。

頂點和索引複製到GL快取

很簡單。_verts和_indices內的資料被複制到GL物件的快取裡。

繪製

繪製的引數是裝載步驟時的TriBatchToDraw內的資訊。每個TriBatchToDraw進行一次繪製,也導致了每次繪製時DrawCall的值加1。

清理

很簡單,就不介紹了。

以上就是渲染的全流程解析。可以總結出,渲染是在每幀結束前進行的;渲染之前是把每幀的所有元素的繪製用命令統一進行儲存,在渲染時讀取這些命令,進行繪製;渲染時還會進行批量繪製的判斷,這能有效降低DrawCall值。

有關降低DrawCall值的學習在這篇文章裡:Cocos2d-x 學習筆記(26) 從原始碼學習 DrawCall 的降低方法


本文連結:https://www.cnblogs.com/deepcho/p/cocos2dx-render.html
<