全文共5000+字,分为8个章节,由本人历时一周整理而来。由于篇幅问题,将本文分为8个章节分开发布。全文 (不) 详细描述了cocoscreator 引擎的2.40版本中,web平台的js部分引擎的渲染流程。请将文章配合源码一起食用!
由于我尚在学习引擎源码中,文章可能有不正确的部分,所以我会不断更新内容。如有错误或补充,请留言交流!
全部章节链接:
三 RenderFlow 的运行逻辑
RenderFlow : 渲染流,用以遍历场景下所有节点,根据每个节点的_renderFlag , 处理节点的位置,颜色,透明度,更新并渲染。
3.1 性能优化
在v1.x版本中,每次渲染都会进行很多动态判断,需要去判断每个节点是否需要更新位置矩阵,是否需要渲染,在这些过程中会有很多无用分支判断,消耗性能。
所以在v2.x版本中,RenderFlow根据渲染过程中调用的频繁度划分出多个渲染状态,比如 Transform,Render,Children 等,而每个渲染状态都对应了一个函数。在 RenderFlow 的初始化过程中,会预先根据这些状态创建好对应的渲染分支,这些分支会把对应的状态依次链接在一起。在渲染前会更新该节点的_renderFlag ,在渲染该节点时就可以直接根据 _renderFlag的值,进行相应分支的处理,不用进行多余的状态判断。
例如一个节点在当前帧需要更新矩阵,以及需要渲染自己,那么这个节点会更新他的 flag 为
node._renderFlag = RenderFlow.FLAG_TRANSFORM | RenderFlow.FLAG_RENDER。
更加详细的内容可见文末的相关链接中 : RenderFlow的性能优化.
3.2 RenderFlow 内的链式方法的创建与调用
RenderFlow中根据 _renderFlag 获取渲染流的代码如下:
function getFlow (flag) {
let flow = null;
let tFlag = FINAL;
while (tFlag > 0) {
if (tFlag & flag)// 如果flag标识匹配,则添加新的渲染流
flow = createFlow(tFlag, flow);// 需要把上一步创建flow传入,作为子流
tFlag = tFlag >> 1;// 标志右移一位
}
return flow;
}
createFlow() 中会根据flag创建对应的渲染流,并加入链中,代码如下:
function createFlow (flag, next) {
let flow = new RenderFlow();
flow._next = next || EMPTY_FLOW;// 将本次创建的flow加入链表首部
// 根据不同的flag设置不同的处理方法
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;
}
RenderFlow是根据node节点上的_renderFlag 来进行不同的渲染流程,所以当node节点上的位置,颜色,透明度等参数改变后,需要同步修改_renderFlag。这样在渲染时会去根据flag处理对应的流程。
3.3 详解 RenderFlow 的不同操作
RenderFlow根据 _renderFlag 创建了链式渲染流,但各个不同的FLAG对应的方法,都做了些什么,下面会详细说明。
- _localTransform 方法
更新本地坐标矩阵。(Tips:节点的位置通过本地坐标矩阵和世界坐标矩阵管理,通过矩阵叉乘来进行高效的坐标转换,具体内容待继续学习了解。。。) - _worldTransform 方法
更新世界坐标矩阵。 - _opacity 方法
处理透明度。 - _color 方法
更新 renderCompent 的颜色 - _updateRenderData 方法
更新渲染数据,调用 Assembler 里的 updateRenderData 方法,主要是更新uv和顶点数据。 - _render 方法
_proto._render = function (node) {
let comp = node._renderComponent;
comp._checkBacth(_batcher, node._cullingMask);
comp._assembler.fillBuffers(comp, _batcher);
this._next._func(node);
};
调用 RenderComponent 的 _checkBacth 检测合批。
调用 Assembler 的 fillBuffers 填充数据。
- _children 方法
遍历子节点进行子节点的渲染流程。 - _postRender 方法
(暂时不理解 todo)
相关链接
RenderFlow的性能优化:http://docs.cocos.com/creator/manual/zh/advanced-topics/render-flow.html#
自定义渲染合批之自定义顶点格式: https://forum.cocos.org/t/demo/95087
自定义RenderFlow,处理背包等场景下drawcall过多:https://forum.cocos.org/t/ui/80026
材质系统:https://docs.cocos.com/creator3d/manual/zh/material-system/overview.html