cocos creator源码阅读(一:device)

本文详细探讨了Director.js中的renderer.render()方法如何触发Cocos渲染流程,并重点剖析了device类的构造函数和核心draw方法,涉及WebGL配置、纹理处理、顶点缓冲和渲染状态管理。通过核心代码实例,揭示了设备兼容性和性能优化关键.

从director.js中的 renderer.render(this._scene,this._deltaTime)就进入了cocos里面的渲染流程了;renderer的定义可以在 renderer/index.js里面找到:

比较重要的成员变量:device,_forward(前向渲染,将试图独享显示到屏幕上依赖device),_flow(渲染流),_handle(渲染前的合批操作,会检查是否符合合批的操作,优化性能,drawcall)

device: 负责针对不同平台的渲染设配的统一接口,里面有webgl的操作,包括上传uniform,drawElement也就是调用webgl的接口的核心类,我就不整篇的贴代码了,我挑选主要的来看看:

这是我整理的device类的脑图:

 下面我选择一些重要的代码说说哈哈:

首先是Device的构造函数:里面有些注释方便大家理解

/**
   * @param {HTMLElement} canvasEL
   * @param {object} opts
   */
  constructor(canvasEL, opts) {
    let gl;

    // default options
    opts = opts || {};
    if (opts.alpha === undefined) {
      opts.alpha = false;
    }
    if (opts.stencil === undefined) {
      opts.stencil = true;
    }
    if (opts.depth === undefined) {
      opts.depth = true;
    }
    if (opts.antialias === undefined) {
      opts.antialias = false;
    }
    // NOTE: it is said the performance improved in mobile device with this flag off.
    if (opts.preserveDrawingBuffer === undefined) {
      opts.preserveDrawingBuffer = false;
    }

    try {
      gl = canvasEL.getContext('webgl', opts)
        || canvasEL.getContext('experimental-webgl', opts)
        || canvasEL.getContext('webkit-3d', opts)
        || canvasEL.getContext('moz-webgl', opts);
    } catch (err) {
      console.error(err);
      return;
    }

    // No errors are thrown using try catch
    // Tested through ios baidu browser 4.14.1
    if (!gl) {
      console.error('This device does not support webgl');
    }

    // statics
    /**
     * @type {WebGLRenderingContext}
     */
    this._gl = gl;
    this._extensions = {};
    // 容量
    this._caps = {}; // capability
    // 状态统计
    this._stats = {
      texture: 0,
      vb: 0,
      ib: 0,
      drawcalls: 0,
    };

    // https://developer.mozilla.org/zh-CN/docs/Web/API/WebGL_API/Using_Extensions
    this._initExtensions([
      // 各向异性过滤 提升图片质量
      'EXT_texture_filter_anisotropic',
      // 提供额外的shader 图片采样方法 lod : level of detail 多细节层次 LOD就是为了支持当物体远离观察者或者物体的重要程度不同,位置不同,速度不同或者视角相关的参数不同需要减少渲染3D模型的复杂度
      'EXT_shader_texture_lod',
      // 与lod配合使用 可以使用shader dFdx,dFdy,fwidth方法
      'OES_standard_derivatives',
      // 暴露图片浮点数像素值
      'OES_texture_float',
      // 允许用浮点数像素值对图片进行过滤 在gl.texParameter中设置 gl.LINEAR...其中的一个
      'OES_texture_float_linear',
      /**
       * var ext = gl.getExtension('OES_texture_half_float');

        var texture = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, texture);

        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, ext.HALF_FLOAT_OES, image);
       * 
       */
      'OES_texture_half_float',
      'OES_texture_half_float_linear',
      // 提供顶点数组对象
      /**
       *  
       * var oes_vao_ext = gl.getExtension('OES_vertex_array_object');
          var vao = oes_vao_ext.createVertexArrayOES();
          oes_vao_ext.bindVertexArrayOES(vao);
       * 
       */
      'OES_vertex_array_object',
      /**
       * 压缩纹理减少了在 GPU 上存储纹理所需的内存量,允许更高分辨率的纹理或更多相同分辨率的纹理。
       */
      'WEBGL_compressed_texture_atc',
      'WEBGL_compressed_texture_etc',
      'WEBGL_compressed_texture_etc1',
      'WEBGL_compressed_texture_pvrtc',
      'WEBGL_compressed_texture_s3tc',
      'WEBGL_depth_texture',
      'WEBGL_draw_buffers',
    ]);
    this._initCaps();
    this._initStates();

    // runtime
    State.initDefault(this);
    this._current = new State(this);
    this._next = new State(this);
    this._uniforms = {}; // name: { value, num, dirty }
    this._vx = this._vy = this._vw = this._vh = 0;
    this._sx = this._sy = this._sw = this._sh = 0;
    this._framebuffer = null;

    //
    this._enabledAttributes = new Array(this._caps.maxVertexAttribs);
    this._newAttributes = new Array(this._caps.maxVertexAttribs);

    for (let i = 0; i < this._caps.maxVertexAttribs; ++i) {
      this._enabledAttributes[i] = 0;
      this._newAttributes[i] = 0;
    }
  }

 核心方法: draw: 注意cur和next的交换,我现在的理解是将一些gl渲染数据写入到next之后,再进行交换,然后清空next的所有状态

/**
   * @method draw
   * @param {Number} base
   * @param {Number} count
   */
  draw(base, count) {
    const gl = this._gl;
    let cur = this._current;
    let next = this._next;

    // commit blend
    _commitBlendStates(gl, cur, next);

    // commit depth
    _commitDepthStates(gl, cur, next);

    // commit stencil
    _commitStencilStates(gl, cur, next);

    // commit cull
    _commitCullMode(gl, cur, next);

    // commit vertex-buffer
    _commitVertexBuffers(this, gl, cur, next);

    // commit index-buffer
    if (cur.indexBuffer !== next.indexBuffer) {
      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, next.indexBuffer && next.indexBuffer._glID !== -1 ? next.indexBuffer._glID : null);
    }

    // commit program
    let programDirty = false;
    if (cur.program !== next.program) {
      if (next.program._linked) {
        gl.useProgram(next.program._glID);
      } else {
        console.warn('Failed to use program: has not linked yet.');
      }
      programDirty = true;
    }

    // commit texture/sampler
    _commitTextures(gl, cur, next);

    // commit uniforms
    for (let i = 0; i < next.program._uniforms.length; ++i) {
      let uniformInfo = next.program._uniforms[i];
      let uniform = this._uniforms[uniformInfo.name];
      if (!uniform) {
        // console.warn(`Can not find uniform ${uniformInfo.name}`);
        continue;
      }

      if (!programDirty && !uniform.dirty) {
        continue;
      }

      uniform.dirty = false;

      // TODO: please consider array uniform: uniformInfo.size > 0

      let commitFunc = (uniformInfo.size === undefined) ? _type2uniformCommit[uniformInfo.type] : _type2uniformArrayCommit[uniformInfo.type];
      if (!commitFunc) {
        console.warn(`Can not find commit function for uniform ${uniformInfo.name}`);
        continue;
      }

      commitFunc(gl, uniformInfo.location, uniform.value);
    }

    if (count) {
      // drawPrimitives
      if (next.indexBuffer) {
        gl.drawElements(
          this._next.primitiveType,
          count,
          next.indexBuffer._format,
          base * next.indexBuffer._bytesPerIndex
        );
      } else {
        gl.drawArrays(
          this._next.primitiveType,
          base,
          count
        );
      }

      // update stats
      this._stats.drawcalls++;
    }

    // TODO: autogen mipmap for color buffer
    // if (this._framebuffer && this._framebuffer.colors[0].mipmap) {
    //   gl.bindTexture(this._framebuffer.colors[i]._target, colors[i]._glID);
    //   gl.generateMipmap(this._framebuffer.colors[i]._target);
    // }

    // reset states 交换缓冲区将next缓冲区显示到屏幕上
    cur.set(next);
    // 清空next缓冲区等待写入新的数据
    next.reset();
  }

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值