Cocos Creator 升级的2.2之后,渲染流程发生了比较大的变化,主要是重构了一些类,属性的位置发生了变化。为了防止日后忘记,先记录下来。
首先在engine/cocos2d/core/renderer/index.js中定义了cc.renderer对象,是一个全局对象,里面存放了一些渲染有关的类定义以及一些全局属性如device
核心的是两个属性,一个是_froward一个是_flow
_flow是一个cc.RenderFlow类(注意:不是实例),cc.RenderFlow定义在engine/cocos2d/core/renderer/render-flow.js中
在初始化的过程中,会创建RenderFlow的实例,并传入_flow.init方法中
let nativeFlow = new renderer.RenderFlow(this.device, this.scene, this._forward);
this._flow.init(nativeFlow);
RenderFlow.init = function (batcher, forwardRenderer) {
_batcher = batcher;
_forward = forwardRenderer;
flows[0] = EMPTY_FLOW;
for (let i = 1; i < FINAL; i++) {
flows[i] = new RenderFlow();
}
};
注意:_batcher就是传入的RenderFlow的实例
渲染开始
入口代码在engine/cocos2d/core/renderer/index.js中的render方法:
render (ecScene, dt) {
this.device.resetDrawCalls();
if (ecScene) {
// walk entity component scene to generate models
this._flow.render(ecScene, dt);
this.drawCalls = this.device.getDrawCalls();
}
},
然后进入
RenderFlow.render = function (scene, dt) {
_batcher.reset();
_batcher.walking = true;
RenderFlow.visitRootNode(scene);
_batcher.terminate();
_batcher.walking = false;
_forward.render(_batcher._renderScene, dt);
};
接下来会进入两个大的流程:
A--------------
RenderFlow.visitRootNode(scene);
这句将进入RenderFlow的实例里面的方法调用
在RenderFlow的实例方法中
核心的方法是_updateRenderData用于更新各级渲染对象的顶点信息等
_render方法,用于执行实际的渲染:
_proto._render = function (node) {
let comp = node._renderComponent;
comp._checkBacth(_batcher, node._cullingMask);
comp._assembler.fillBuffers(comp, _batcher);
this._next._func(node);
};
其中,_renderComponent属性是继承自RenderComponent的对象会指向自己
代码在core/components/CCRenderComponent.js
onEnable () {
if (this.node._renderComponent) {
this.node._renderComponent.enabled = false;
}
this.node._renderComponent = this;
this.node.on(cc.Node.EventType.SIZE_CHANGED, this._onNodeSizeDirty, this);
this.node.on(cc.Node.EventType.ANCHOR_CHANGED, this._onNodeSizeDirty, this);
this.node._renderFlag |= RenderFlow.FLAG_RENDER | RenderFlow.FLAG_UPDATE_RENDER_DATA | RenderFlow.FLAG_OPACITY_COLOR;
}
然后是调用_checkBacth方法
_checkBacth (renderer, cullingMask) {
let material = this.sharedMaterials[0];
if ((material && material.getHash() !== renderer.material.getHash()) ||
renderer.cullingMask !== cullingMask) {
renderer._flush();
renderer.node = material.getDefine('CC_USE_MODEL') ? this.node : renderer._dummyNode;
renderer.material = material;
renderer.cullingMask = cullingMask;
}
}
该方法用于更新组件的材质,如果材质的hash发生变化,意味着材质更新了,就需要重新设置一些配置,即renderer._flush()
代码在core/renderer/webgl/model-batcher.js下面
_flush () {
let material = this.material,
buffer = this._buffer,
indiceCount = buffer.indiceOffset - buffer.indiceStart;
if (!this.walking || !material || indiceCount <= 0) {
return;
}
let effect = material.effect;
if (!effect) return;
// Generate ia
let ia = this._iaPool.add();
ia._vertexBuffer = buffer._vb;
ia._indexBuffer = buffer._ib;
ia._start = buffer.indiceStart;
ia._count = indiceCount;
// Generate model
let model = this._modelPool.add();
this._batchedModels.push(model);
model.sortKey = this._sortKey++;
model._cullingMask = this.cullingMask;
model.setNode(this.node);
model.setEffect(effect, this.customProperties);
model.setInputAssembler(ia);
this._renderScene.addModel(model);
buffer.forwardIndiceStartToOffset();
},
其中model.setEffect(effect, this.customProperties);这句代码进入
renderer/scene/model.js下面
setEffect(effect, customProperties) {
this._effect = effect;
let defines = this._defines;
let uniforms = this._uniforms;
defines.length = 0;
uniforms.length = 0;
if (effect) {
defines.push(effect._defines);
uniforms.push(effect._properties);
}
if (customProperties) {
defines.push(customProperties._defines);
uniforms.push(customProperties._properties);
}
}
这里会将effect中的定义取出来
B--------------
_forward.render(_batcher._renderScene, dt);进入
在进入renderer/renderers/forward-renderer.js
render (scene, dt) {
this.reset();
if (!CC_EDITOR) {
this._time[0] += dt;
this._device.setUniform('cc_time', this._time);
}
this._updateLights(scene);
const canvas = this._device._gl.canvas;
for (let i = 0; i < scene._cameras.length; ++i) {
let view = this._requestView();
let width = canvas.width;
let height = canvas.height;
let camera = scene._cameras.data[i];
camera.extractView(view, width, height);
}
// render by cameras
this._viewPools.sort((a, b) => {
return (a._priority - b._priority);
});
for (let i = 0; i < this._viewPools.length; ++i) {
let view = this._viewPools.data[i];
this._render(view, scene);
}
}
然后进入renderer/core/base-renderer.js中的_render方法此处代码省略
进入后调用engine/cocos2d/core/renderer/render-flow.js 中的_drawItems然后是
engine/cocos2d/core/renderer/base-renderer.js 中的_draw
然后进入engine/cocos2d/core/renderer/gfx/device.js 中的draw