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