查看原文
其他

CreatorPrimer| CustomMaterial.js源码分析

张晓衡 Creator星球游戏开发社区 2021-08-09

1. 回顾


《ShaderHelper组件速递》一篇我们介绍了ShaderHelper组件的使用,以及如何定义一个shader程序并添加到ShaderHelper组件的program枚举属性中,这里我们再简单回顾下shader模板对象定义:

  1. /**

  2. * shader模板

  3. **/

  4. const renderEngine = cc.renderer.renderEngine;

  5. const renderer = renderEngine.renderer;

  6. //定义一个shader对象

  7. const shader = {

  8.    //名字必须字段

  9.    name: "xxx",

  10.    //着色器代码中需要与js交互的参数名字与数据类型

  11.    params: [

  12.        {name: 'yyy', type: renderer.PARAM_FLOAT},

  13.        {name: 'zzz', type: renderer.PARAM_FLOAT2}

  14.    ],

  15.    //着色器中使用到的define定义,非必要字段

  16.    defines: [],

  17.    //start回调,此可以初始化着色器中的参数

  18.    start(sprite, material) { ... },

  19.    //update每帧回调,如果是动态效果,可以在此设置Shader参数

  20.    update(sprite, material) { ... },

  21.    //vert顶点着色器代码,它是一个字符串

  22.    vert: '...',

  23.    //frag片元着色器

  24.    frag: `...`

  25. };

  26. //将shader对象添加到自定义材质中

  27. let CustomMaterial = require('CustomMaterial');

  28. CustomMaterial.addShader(shader);

  1. 最为基本的3个属性:name、vert、frag

  2. 如果要控制着色器中的参数变量需定义params字段

  3. 如果要控制define变量定义defines字段

  4. 如果要为param变量设置初始值可以在start回调函数中完成

  5. 如果需要每帧控制参数可以在update回调函数中完成

更多的使用细节请参看前一篇文章与github上的源码,今天分享的内容是ShaderHelper组件中的核心CustomMaterial源码分析。

2. CustomMaterial自定义材质系统


ShaderHelper组件只是对Cocos论坛Colin提供的CustomMaterial的调用,CustomMaterial又是对Cocos Creator引擎中的渲染引擎、材质系统API的运用,其中四个重要的对象:

Material(材质)、Effect(表现)、Technique(技术)、Pass(过程)

我们对CustomMaterial类的主要成员有个大概的认识,首先CustomMaterial继承自Cocos Creator引擎的Material类,同时它实现对Effect的实例化,看下面代码:

  1. ...

  2. var CustomMaterial = (function (Material$$1) {

  3.    ...

  4.    //继承Material

  5.    cc.js.extend(CustomMaterial, Material$$1);

  6.    //定义了三个属性effect、texture、color

  7.    var prototypeAccessors = {

  8.        effect:  { configurable: true },

  9.        texture: { configurable: true },

  10.        color:   { configurable: true }

  11.    };

  12.    ...

  13.    Object.defineProperties(CustomMaterial.prototype, prototypeAccessors);

  14.    ...

  15. }(Material));

上面我将干扰代码移除,基中最为关键的是effect属性的定义,我们看Effect是怎么实例化的:

  1. //实现化Effect对象

  2. this._effect = new renderer.Effect(

  3.    [ mainTech ],

  4.    {},

  5.    defines, //第三个参数defines就是我们之前定义的shader.dfines字段

  6. );

  7. this._texture = null;

  8. this._color = { r: 1, g: 1, b: 1, a: 1 };

  9. ...

  10. //定义effect\texutre\color的get方法

  11. prototypeAccessors.effect.get = function () {

  12.    return this._effect;

  13. };

  14. prototypeAccessors.texture.get = function () {

  15.    return this._texture;

  16. };

  17. prototypeAccessors.color.get = function () {

  18.    return this._color;

  19. };

上面代码实现化了_effect、texture、_color三个对象为内部私有成员,并为它们各自实现了get方法,使其可以被外部访问。

3. Effect的实例化


texture与color的初始化比较简,但Eeffect实例化需要三个参数,看下引擎源码:

  1. //--------------CustomMaterail.js-----------------

  2. this._effect = new renderer.Effect(

  3.    [ mainTech ],

  4.    {},  

  5.    defines, //defines就是我们之前定义的shader.dfines字段

  6. );

  7. //-----------------render-engine.js-----------------

  8. //看下Effect类构建函数参数

  9. var Effect = function Effect(techniques, properties, defines) {

  10.   ...

  11. };

Effect中三个数组分别是:techniques, properties, defines,其中defines就是我们前面shader对象中的defines。techniques是一个数组,我们接下来看CustomMaterial源码中是怎么创建它的。

4. Technique的实例化


Effect类的第一个参数需要Technique的数组,我们看Technique的创建:

  1. //--------------------CustomMaterial.js-----------------------

  2. //定义了两个默认的参数:texture、color

  3. var techParams = [

  4.    { name: 'texture', type: renderer.PARAM_TEXTURE_2D },

  5.    { name: 'color', type: renderer.PARAM_COLOR4 }

  6. ];

  7. //params就是之前定义的shader.params

  8. if (params) {

  9.    techParams = techParams.concat(params);

  10. }

  11. //实例化Technique,我们之前定义的params成了Technique的参数

  12. var mainTech = new renderer.Technique(

  13.    ['transparent'], //stages参数,暂时也没搞懂具体意思

  14.    techParams,    

  15.    [pass]

  16. );

  17. //--------------------render-engine.js-----------------------

  18. //Technique类的构建函数

  19. var Technique = function Technique(stages, parameters, passes, layer) {

  20. ...

  21. }

Technique的构建函数需要4个参数,上面代码中给了前三个,其中techParams就是我们前面shader对象中定义的params字段,stages参数这里给的是['transparent']我暂时也没搞懂是什么意思,先暂时不管它。passes又是什么鬼呢?我看再看pass的创建过程。

5. Pass的实例化


创建Technique又需要一个passes数组,再看代码pass的实例化过程:

  1. //我们之前定义的shader.name成了Pass的构造参数

  2. var pass = new renderer.Pass(shaderName);

  3. //下面的函数调用Shawn也不太了解,这里就不解释了,等弄明白了再回来

  4. pass.setDepth(false, false);

  5. pass.setCullMode(gfx.CULL_NONE);

  6. pass.setBlend(

  7.    gfx.BLEND_FUNC_ADD,

  8.    gfx.BLEND_SRC_ALPHA, gfx.BLEND_ONE_MINUS_SRC_ALPHA,

  9.    gfx.BLEND_FUNC_ADD,

  10.    gfx.BLEND_SRC_ALPHA, gfx.BLEND_ONE_MINUS_SRC_ALPHA

  11. );

说话实Pass的实例化我也不太了解,通过字面意思猜测是设置材质相关的参数,参考了论坛中Panda提供的heartfelt工程,也是同样的写法。

6. 小结


本篇的内容有些烧脑,特别是对于像Shawn这种从来不怎么关心底层渲染的人来说在初次读源码完全是一脸的蒙逼。我们暂且不纠结细节,从整体上理清材质系统的框架结构,请看下图:

Shawn也是在不断坚持的过程中结合Panda对Cocos Creator 2.x新渲染器的介绍,以及几次Colin热心的指导交流,后来又拜读了麒麟子大神的《Thinking in Unity3D:材质系统概览》一文,才让我对材质系统有了初步的理解,至此才有幸能初步读懂CustomMaterial源码,在此感谢以上大佬们!



如果觉得众,感谢

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存