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);
- 最爲基本的3個屬性:name、vert、frag
- 如果要控制着色器中的參數變量需定義params字段
- 如果要控制define變量定義defines字段
- 如果要爲param變量設置初始值可以在start回調函數中完成
- 如果需要每幀控制參數可以在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,其中關鍵在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. 小結
我們暫且不糾結細節,從整體上理清楚Cocos Creator 2.x材質系統的框架結構,請看下圖:
本篇的內容有些燒腦,特別是對於像Shawn這種從來不怎麼關心底層渲染的人來說在初次讀源碼完全是一臉的蒙逼樣。在不斷堅持的過程中結合Panda對2.x新渲染器的介紹;以及幾次與Colin的交流指導;後來又拜讀了麒麟子大神的《Thinking in Unity3D:材質系統概覽》一文才讓我對材質系統有了初步的理解,至此纔有幸能初步讀懂CustomMaterial源碼,在此感謝以上大佬們!
如果覺得衆號上的文章對您或您身邊的朋友有幫助,感謝分享給他們,願我們一起成長!