从CCDirector.js中的mainLoop我们了解到 renderer.render是实际参与渲染的方法:顺着线索我们找到了RenderFlow这个类:下面是我整理的部分关键的方法和属性:
首先看一下initWebGL方法:
initWebGL (canvas, opts) { require('./webgl/assemblers'); const ModelBatcher = require('./webgl/model-batcher'); this.Texture2D = gfx.Texture2D; this.canvas = canvas; this._flow = cc.RenderFlow; if (CC_JSB && CC_NATIVERENDERER) { // native codes will create an instance of Device, so just use the global instance. this.device = gfx.Device.getInstance(); this.scene = new renderer.Scene(); let builtins = _initBuiltins(this.device); this._forward = new renderer.ForwardRenderer(this.device, builtins); let nativeFlow = new renderer.RenderFlow(this.device, this.scene, this._forward); this._flow.init(nativeFlow); } else { let Scene = require('../../renderer/scene/scene'); let ForwardRenderer = require('../../renderer/renderers/forward-renderer'); this.device = new gfx.Device(canvas, opts); this.scene = new Scene(); let builtins = _initBuiltins(this.device); // 前向渲染对象负责将view视图通过device渲染都屏幕上 this._forward = new ForwardRenderer(this.device, builtins); // 渲染前批处理,优化性能,降低drawcall this._handle = new ModelBatcher(this.device, this.scene); // 初始化渲染流对象依赖 前向渲染对象和批处理 this._flow.init(this._handle, this._forward); console.log(`scene is `,this.scene,' and device is ',this.device,' and _flow is ',this._flow,' and _handle is ',this._handle); } },
再来看看主角方法render(scene,dt):
render (ecScene, dt) { /** 重置drawcall */ this.device.resetDrawCalls(); if (ecScene) { // walk entity component scene to generate models /** 调用渲染静态函数 */ this._flow.render(ecScene, dt); this.drawCalls = this.device.getDrawCalls(); } },
继续跟进看看render: 下一节着重看看这个前向渲染 _forward
RenderFlow.render = function (rootNode, dt) { _batcher.reset(); _batcher.walking = true; /** 递归遍历根节点 */ RenderFlow.visitRootNode(rootNode); _batcher.terminate(); _batcher.walking = false; // 将batcher中的渲染数据渲染到屏幕 _forward渲染数据需要用到合批的渲染数据两个类有相互依赖的关系 _forward.render(_batcher._renderScene, dt); };
看看渲染流的init静态方法:
RenderFlow.init = function (batcher, forwardRenderer) { _batcher = batcher; _forward = forwardRenderer; // 这里flows是一个包含了1025个RenderFlow对象,而每一个对象又是一个渲染链表对象 flows[0] = EMPTY_FLOW; for (let i = 1; i < FINAL; i++) { flows[i] = new RenderFlow(); } };
看看init方法:渲染流真正执行_func的时候会调用它,是根据节点的渲染标识进行创建的
function init (node) { // 拿到节点的渲染标识 let flag = node._renderFlag; // 根据节点身上的渲染标识进行创建渲染流 let r = flows[flag] = getFlow(flag); // 执行对应的渲染流函数 r._func(node); }
看看getFlow函数,就是在这里创建了渲染流链表:
function getFlow (flag) { let flow = null; let tFlag = FINAL; while (tFlag > 0) { if (tFlag & flag) // 创建渲染流 将上一个flow传入构成一个链 flow = createFlow(tFlag, flow); tFlag = tFlag >> 1; } return flow; }
每一次createFlow都会将当前的flow置为头节点
function createFlow (flag, next) { let flow = new RenderFlow(); // 将当前创建的渲染流置于链表的头部 flow._next = next || EMPTY_FLOW; switch (flag) { case DONOTHING: flow._func = flow._doNothing; break; case BREAK_FLOW: flow._func = flow._doNothing; break; case LOCAL_TRANSFORM: flow._func = flow._localTransform; break; case WORLD_TRANSFORM: flow._func = flow._worldTransform; break; case OPACITY: flow._func = flow._opacity; break; case COLOR: flow._func = flow._color; break; case UPDATE_RENDER_DATA: flow._func = flow._updateRenderData; break; case RENDER: flow._func = flow._render; break; case CHILDREN: flow._func = flow._children; break; case POST_RENDER: flow._func = flow._postRender; break; } return flow; }