CreatorPrimer| CustomMaterial.js源码分析

1. 回顾


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

/**
 * shader模板
 **/
const renderEngine = cc.renderer.renderEngine;
const renderer = renderEngine.renderer;

//定义一个shader对象
const shader = {
    //名字必须字段 
    name: "xxx",
    //着色器代码中需要与js交互的参数名字与数据类型
    params: [
        {name: 'yyy', type: renderer.PARAM_FLOAT},
        {name: 'zzz', type: renderer.PARAM_FLOAT2}
    ],
    //着色器中使用到的define定义,非必要字段
    defines: [],
    //start回调,此可以初始化着色器中的参数
    start(sprite, material) { ... },
    //update每帧回调,如果是动态效果,可以在此设置Shader参数
    update(sprite, material) { ... }, 
    //vert顶点着色器代码,它是一个字符串
    vert: '...',
    //frag片元着色器
    frag: `...`
};
//将shader对象添加到自定义材质中
let CustomMaterial = require('CustomMaterial');
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的实例化,看下面代码:

...
var CustomMaterial = (function (Material$$1) {
    ...
    //继承Material
    cc.js.extend(CustomMaterial, Material$$1);

    //定义了三个属性effect、texture、color
    var prototypeAccessors = { 
        effect:  { configurable: true }, 
        texture: { configurable: true }, 
        color:   { configurable: true } 
    };
    ...
    Object.defineProperties(CustomMaterial.prototype, prototypeAccessors);
    ...
}(Material));

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

//实现化Effect对象
this._effect = new renderer.Effect(
    [ mainTech ],
    {},
    defines, //第三个参数defines就是我们之前定义的shader.dfines字段
);
this._texture = null;
this._color = { r: 1, g: 1, b: 1, a: 1 };
...
//定义effect\texutre\color的get方法
prototypeAccessors.effect.get = function () {
    return this._effect;
};
prototypeAccessors.texture.get = function () {
    return this._texture;
};
prototypeAccessors.color.get = function () {
    return this._color;
};

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

3. Effect的实例化


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

//--------------CustomMaterail.js-----------------
this._effect = new renderer.Effect(
    [ mainTech ], 
    {},  
    defines, //defines就是我们之前定义的shader.dfines字段
);

//-----------------render-engine.js-----------------
//看下Effect类构建函数参数
var Effect = function Effect(techniques, properties, defines) {
   ...
};

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

4. Technique的实例化


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

//--------------------CustomMaterial.js-----------------------
//定义了两个默认的参数:texture、color
var techParams = [
    { name: 'texture', type: renderer.PARAM_TEXTURE_2D },
    { name: 'color', type: renderer.PARAM_COLOR4 }
];
//params就是之前定义的shader.params
if (params) {
    techParams = techParams.concat(params);
}
//实例化Technique,我们之前定义的params成了Technique的参数
var mainTech = new renderer.Technique(
    ['transparent'], //stages参数,暂时也没搞懂具体意思
    techParams,     
    [pass]
);

//--------------------render-engine.js-----------------------
//Technique类的构建函数
var Technique = function Technique(stages, parameters, passes, layer) {
...
}

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

5. Pass的实例化


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

//我们之前定义的shader.name成了Pass的构造参数
var pass = new renderer.Pass(shaderName);
//下面的函数调用Shawn也不太了解,这里就不解释了,等弄明白了再回来
pass.setDepth(false, false);
pass.setCullMode(gfx.CULL_NONE);
pass.setBlend(
    gfx.BLEND_FUNC_ADD,
    gfx.BLEND_SRC_ALPHA, gfx.BLEND_ONE_MINUS_SRC_ALPHA,
    gfx.BLEND_FUNC_ADD,
    gfx.BLEND_SRC_ALPHA, gfx.BLEND_ONE_MINUS_SRC_ALPHA
);

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

6. 小结


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

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

原文发布于微信公众号 - Creator星球游戏开发社区(creator-star)

原文发表时间:2018-11-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券