文章目录

  • 一、前言
  • 二、流程图
  • 三、源码解析
  • 1、CCDirector.js (导演类)
  • 1.1 drawScene 方法 (绘画场景)
  • 2、RendererWebGL.js (渲染类)
  • 2.1 clearRenderCommands 方法 (清空渲染命令队列)
  • 2.2 rendering 方法 (根据渲染命令队列,重新渲染画布)
  • 2.3 _uploadBufferData 方法 (处理渲染命令的数据)
  • 3、CCNode.js (节点类)
  • 3.1 visit 方法 (递归子节点的visit方法,把对应的节点渲染命令添加到 渲染器)
  • 4、CCNodeWebGLRenderCmd.js (节点渲染命令)
  • 4.1 uploadData 方法 (把数据传到渲染类)
  • 四、结束语


一、前言

嗨,大家好,我是思航。今天打算分享一下 cocos2d-x-html的渲染流程。

二、流程图

docxtemplater 渲染html html渲染流程_子节点

三、源码解析

1、CCDirector.js (导演类)

导演类,控制游戏每帧执行的逻辑

1.1 drawScene 方法 (绘画场景)

drawScene: function () {
        var renderer = cc.renderer;

        ...

        // draw the scene
        if (this._runningScene) {
            // 需要更新渲染命令队列
            if (renderer.childrenOrderDirty) {
                // 清空渲染命令队列
                cc.renderer.clearRenderCommands();
                cc.renderer.assignedZ = 0;
                this._runningScene._renderCmd._curLevel = 0; //level start from 0;
                // 调用visit,重新生成 渲染命令队列
                this._runningScene.visit();
                // 重置标识 childrenOrderDirty改为 false
                renderer.resetFlag();
            } else if (renderer.transformDirty()) {
                renderer.transform();
            }
        }
		// 清空画布
        renderer.clear();
        ...
        // 根据渲染命令队列,重新渲染画布
        renderer.rendering(cc._renderContext);
    },

2、RendererWebGL.js (渲染类)

渲染类,其成员变量 _renderCmds 为存放渲染命令的队列。下面介绍几个使用的到方法。

2.1 clearRenderCommands 方法 (清空渲染命令队列)

clearRenderCommands: function () {
        // Copy previous command list for late check in rendering
        this._renderCmds.length = 0;
    },

2.2 rendering 方法 (根据渲染命令队列,重新渲染画布)

通过遍历渲染命令队列,执行每个渲染命令的_uploadBufferData,然后进行 _batchRendering

rendering: function (ctx, cmds) {
        var locCmds = cmds || this._renderCmds,
            i, len, cmd,
            context = ctx || cc._renderContext;

        // Reset buffer for rendering
        context.bindBuffer(gl.ARRAY_BUFFER, null);

        for (i = 0, len = locCmds.length; i < len; ++i) {
            cmd = locCmds[i];
            if (!cmd.needDraw()) continue;

            if (cmd.uploadData) {
                this._uploadBufferData(cmd);
            }
            else {
                if (_batchingSize > 0) {
                    this._batchRendering();
                }
                cmd.rendering(context);
            }
        }
        this._batchRendering();
        _batchedInfo.texture = null;
    }

2.3 _uploadBufferData 方法 (处理渲染命令的数据)

_uploadBufferData: function (cmd) {
		// 当前批次大小大于最大,进行一次渲染
        if (_batchingSize >= _maxVertexSize) {
            this._batchRendering();
        }

        // Check batching
        var node = cmd._node;
        var texture = node._texture || (node._spriteFrame && node._spriteFrame._texture);
        var blendSrc = node._blendFunc.src;
        var blendDst = node._blendFunc.dst;
        var glProgramState = cmd._glProgramState;
        if (_batchBroken ||
            _batchedInfo.texture !== texture ||
            _batchedInfo.blendSrc !== blendSrc ||
            _batchedInfo.blendDst !== blendDst ||
            _batchedInfo.glProgramState !== glProgramState) {
            // Draw batched elements
            // 如果不能合并,直接执行渲染(调用一次,一个drawcall)
            this._batchRendering();
            // Update _batchedInfo
            _batchedInfo.texture = texture;
            _batchedInfo.blendSrc = blendSrc;
            _batchedInfo.blendDst = blendDst;
            _batchedInfo.glProgramState = glProgramState;
            _batchBroken = false;
        }

        // Upload vertex data
        // 执行渲染命令的uploadData
        var len = cmd.uploadData(_vertexDataF32, _vertexDataUI32, _batchingSize * _sizePerVertex);
        if (len > 0) {
            this._increaseBatchingSize(len, cmd.vertexType);
        }
    },

3、CCNode.js (节点类)

节点类,有很多方法,我们这边重点看下 visit 方法

3.1 visit 方法 (递归子节点的visit方法,把对应的节点渲染命令添加到 渲染器)

visit: function (parent) {
        var cmd = this._renderCmd, parentCmd = parent ? parent._renderCmd : null;

        // quick return if not visible
        if (!this._visible) {
            cmd._propagateFlagsDown(parentCmd);
            return;
        }

        var renderer = cc.renderer;
        // 同步父节点的Dirty状态,这里先不细介绍
        cmd.visit(parentCmd);

        var i, children = this._children, len = children.length, child;
        if (len > 0) {
            if (this._reorderChildDirty) {
                this.sortAllChildren();
            }
            // 先执行 zOrder < 0 的,这边可以先忽略
            // draw children zOrder < 0
            for (i = 0; i < len; i++) {
                child = children[i];
                if (child._localZOrder < 0) {
                    child.visit(this);
                }
                else {
                    break;
                }
            }
			// 把节点对应的渲染命令压入到 渲染队列里面
            renderer.pushRenderCommand(cmd);
            for (; i < len; i++) {
            	// 在执行子节点的visit,这样就会吧子节点的渲染命令也压进入
                children[i].visit(this);
            }
        } else {
            renderer.pushRenderCommand(cmd);
        }
        cmd._dirtyFlag = 0;
    },

4、CCNodeWebGLRenderCmd.js (节点渲染命令)

4.1 uploadData 方法 (把数据传到渲染类)

这里我们看下 CCSpriteWebGLRenderCmd.js 里面的uploadData方法

// 参数为RendererWebGL.js的变量,把渲染命令的数据修改到里面
// 这里具体数据不展开介绍,后面另开文章专门介绍
proto.uploadData = function (f32buffer, ui32buffer, vertexDataOffset) {
        var node = this._node, locTexture = node._texture;
        if (!(locTexture && locTexture._textureLoaded && node._rect.width && node._rect.height) || !this._displayedOpacity)
            return 0;

        // Fill in vertex data with quad information (4 vertices for sprite)
        var opacity = this._displayedOpacity;
        var r = this._displayedColor.r,
            g = this._displayedColor.g,
            b = this._displayedColor.b;
        if (node._opacityModifyRGB) {
            var a = opacity / 255;
            r *= a;
            g *= a;
            b *= a;
        }
        this._color[0] = ((opacity << 24) | (b << 16) | (g << 8) | r);
        var z = node._vertexZ;

        var vertices = this._vertices;
        var i, len = vertices.length, vertex, offset = vertexDataOffset;
        for (i = 0; i < len; ++i) {
            vertex = vertices[i];
            f32buffer[offset] = vertex.x;
            f32buffer[offset + 1] = vertex.y;
            f32buffer[offset + 2] = z;
            ui32buffer[offset + 3] = this._color[0];
            f32buffer[offset + 4] = vertex.u;
            f32buffer[offset + 5] = vertex.v;
            offset += 6;
        }

        return len;
    };

四、结束语

通过上面的介绍,我们可以了解到游戏渲染的整体流程。这边节点使用了CCNode.js来介绍,这只是个基类,具体的每个控件以及渲染命令都不一样,这个后面课程继续介绍。今天就到这里, 下次再见!