Cocos Creator 2.2 的渲染流程(原生渲染)

2022-07-28 15:54:33 浏览数 (2)

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方法中

代码语言:javascript复制
            let nativeFlow = new renderer.RenderFlow(this.device, this.scene, this._forward);
            this._flow.init(nativeFlow);
代码语言:javascript复制
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方法:

代码语言:javascript复制
    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();
        }
    },

然后进入

代码语言:javascript复制
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方法,用于执行实际的渲染:

代码语言:javascript复制
_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

代码语言:javascript复制
    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方法

代码语言:javascript复制
    _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下面

代码语言:javascript复制
    _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下面

代码语言:javascript复制
  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

代码语言:javascript复制
  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

0 人点赞