CocosCreator Shader筆記 (TheBookOfShader、漸變色、攻擊閃白特效)


 cocos版本:2.4.4

參考:

Cocos2D文檔: 材質資源  、 Effect

Cocos3D文檔:  材質 、 常用 shader 內置 Uniform

基礎知識:    The Book of Shader 中文版

水友文章:    學習shader的入門筆記

                      cocos2.3 Shader編寫示例

           Cocos Creator Shader Effect 系列

                  從被攻擊閃白shader到相關原創整理,以及相關學習資料整理

        TheBookOfShader開始

        OpenGL shader GLSL 中文手冊 

目錄

一 Shader

二 Cocos中的Shader

三 學習TheBookOfShader,並在cocos中實現書中效果

 

一 Shader

着色器(Shader)是用來實現圖像渲染的,用來替代固定渲染管線的可編輯程序。其中Vertex Shader(頂點着色器)主要負責頂點的幾何關系等的運算,Pixel Shader(像素着色器)主要負責片源顏色等的計算。

着色器替代了傳統的固定渲染管線,可以實現3D圖形學計算中的相關計算,由於其可編輯性,可以實現各種各樣的圖像效果而不用受顯卡的固定渲染管線限制。

理解上Shader是一段代碼,通過編程告訴GPU如何繪制頂點和顏色,從而實現各種各樣的圖像效果,例如彩色字體、按鈕置灰、圖片馬賽克、描邊、動態讓圖片扭動起來等等。

 

二  Cocos中的Shader

每張圖片cc.Sprite或文本cc.Label都有一個默認Materials材質。

材質資源可以用來控制渲染組件在場景中的視覺效果。簡單來說材質就是用來指定物體表面的特性,如顏色、光亮程度、自發光度以及不透明度等。

 

材質對應的Effect文件,里面就是shader代碼

 

Shader語言有3種:

1.基於OpenGL的OpenGL Shading Language,簡稱GLSL。

2.基於DirectX的High Level Shading Language,簡稱HLSL。

3. NVIDIA公司的C for Graphic,簡稱Cg語言。

 

Cocos采用的是YAML和GLSL,YAML聲明控制流程清單,GLSL聲明實際的shader片段。具體查看Effect語法

builtin-2d-sprite.effect完整代碼如下:

CCEffect編寫聲明

其中properties可以自定義外部變量,例如自定義顏色變量,定義后可在屬性面板選擇顏色。

 

CCPrograms vs

獲取頂點數據,向下一個渲染管道傳遞數據。

 

 這段代碼之后基本不用動,主要理解是兩個變量:

out vec4 v_color; //當前node節點顏色。

out vec2 v_uv0;   //坐標,原點在左上角,xy軸坐標分別通過v_uv0.x 和v_uv0.y獲取。

 CCProgram fs

大部分自定義shader邏輯代碼寫在這里面。下面的代碼對cocos的圖片進行采樣,然后和node節點顏色混合后輸出,實現普通builtin-2d-sprite效果。

CCTexture(texture,v_uv0,o); //對圖片進行采樣,顏色存儲在o里

o *= v_color;                          // o和node節點顏色v_color進行混合

gl_FragColor = i;                   //輸出顏色

 

 

那么問題來了,里面的其它變量都是什么意思...都有哪些內置函數和變量,cocosAPI搜索根本查不到這些....哪里看這些API的文檔...

Cocos Effect語法:Effect語法

Cocos常用 shader 內置 Uniform:內置Uniform

GLSL語法:OpenGL shader GLSL 中文手冊

 

三 學習TheBookOfShader,並在cocos中實現書中效果

 打開 The Book of Shader 中文版 ,開始學習。

 

邊看教程邊寫例子。在cocos中新建測試用的Effect和Material,分別命名為TestMaterial和TestEffect。

 

選擇TestMaterial,設置Effect屬性為TestEffect

 

選擇任意一張圖片,賦值Materials屬性為TestMaterial

 

在資源管理器,選擇這個圖片,將packable的勾去掉。如果這個打包的勾被勾選,則shader在發布后出問題。

 

雙擊TestEffect,則可以在vs code打開並編輯TestEffect文件,但是文件都是白字,沒有語法高亮。需要安裝一個插件來支持代碼高亮。

 

vscode中安裝Cocos Effect插件

這個插件可以高亮effect文件,方便閱讀代碼。選擇查看-擴展

 

搜索Cocos Effect並安裝

 

安裝后代碼有了顏色

 

第一個例子,將圖片變成紅色

修改TestEffect的CCProgram fs的最后一行gl_FragColor=o改為gl_FragColor = vec4(1.0,0,1.0,1.0)

vec4的4個參數分別代表顏色通道(red, green,blue,alpha),顏色值是范圍0-1,注意不要寫整數1,要寫浮點數1.0。

CCProgram fs %{
  precision highp float;
  
  #include <alpha-test>
  #include <texture>
  #include <cc-global>
  #include <cc-local>

  in vec4 v_color;
  #if USE_TEXTURE
   in vec2 v_uv0;
   uniform sampler2D texture;
  #endif
  
  uniform color{
    vec4 imgColor;
  };

 
  void main () {
    vec4 o = vec4(1, 1, 1, 1);

    #if USE_TEXTURE
      CCTexture(texture, v_uv0, o);
    #endif

    o *= v_color;

    ALPHA_TEST(o); 

    gl_FragColor = vec4(1.0,0,1.0,1.0)*o; 
  } 
}%

關於顏色值vec4的訪問,下圖中訪問方式是等效的。

 

 第二個例子,圖片紅色閃爍

cc_time.x就是書里的u_time,表示游戲的運行時間。o.r就是紅色通道值, abs(sin(cc_time.x))就是利用余弦函數,隨着游戲時間增加,紅色通道值一直在0-1之間變化,從而形成了閃爍效果。

  void main () {
    vec4 o = vec4(1, 1, 1, 1);

    #if USE_TEXTURE
      CCTexture(texture, v_uv0, o);
    #endif

    o *= v_color;

    ALPHA_TEST(o); 

    o.r = abs(sin(cc_time.x));
    gl_FragColor = o;
  }

 

  

 第三個例子,漸變色

v_uv0就是書中的gl_FragCoord.xy/u_resolution,表示坐標。

v_uv0.x和v_uv0.y值是從0-1變化的,gl_FragColor = vec4(v_uv0.x,v_uv0.y,1.0,1.0)表示紅色和綠色通道值從左上角到右下角由0-1變化。

  void main () {
    vec4 o = vec4(1, 1, 1, 1);

    #if USE_TEXTURE
      CCTexture(texture, v_uv0, o);
    #endif

    o *= v_color;

    ALPHA_TEST(o); 

    gl_FragColor = vec4(v_uv0.x,v_uv0.y,1.0,1.0);
  } 

因為沒有計算cocos圖片顏色o,所以這里是單純的顏色值。

 

gl_FragColor = vec4(v_uv0.x,v_uv0.y,1.0,1.0)理解起來很抽象,我們代入幾個值到公式里看看就知道規律了。

x坐標                y坐標                顏色值                            結果

v_uv0.x=0        v_uv0.y=0         vec4(0,0,1.0,1.0)             (0,0)表示左上角,顏色值藍色

v_uv0.x = 1.0   v_uv0.y=1.0      vec4(1.0,1.0,1.0,1.0)       (1,1)表示右下角,顏色值白色

v_uv0.x = 0.5   v_uv0.y=0.5      vec4(0.5,0.5,1.0,1.0)       (0.5,0.5)表示中間,顏色值淡紫色

 

 

漸變色帶圖,這里計算了圖片本身的顏色o

  void main () {
    vec4 o = vec4(1, 1, 1, 1);

    #if USE_TEXTURE
      CCTexture(texture, v_uv0, o);
    #endif

    o *= v_color;

    ALPHA_TEST(o); 

    gl_FragColor = vec4(v_uv0.x*o.r,v_uv0.y*o.g,o.b,o.a);
  } 

 

 

 

 第四個例子,畫一條綠線

smoothstep(起始值A,結束值B,插值t)  ,參考cocos里的cc.Vec3.lerp函數,大致smoothstep(A,B,t)返回值應該是A + (B-A)*t 

  float plot(vec2 st) {    
    return smoothstep(0.02, 0.0, abs(st.y - st.x));
  }
 
  void main () {

    float y = v_uv0.x;

    vec3 color = vec3(y);

    // Plot a line
    float pct = plot(v_uv0);
    color = (1.0-pct)*color+pct*vec3(0.0,1.0,0.0);
    gl_FragColor = vec4(color,1.0);
  } 

 

 第五個例子,圓

distance計算距離, distance(v_uv0, vec2(0.5))得到坐標離圖片中心點的距離,距離越遠值越大,越接近白色;距離越近值越小,越接近黑色。

  void main () {

    float pct = 0.0;

    pct = distance(v_uv0,vec2(0.5));

    vec3 color = vec3(pct);

    gl_FragColor = vec4( color, 1.0 );
  } 

 

畫圓形,dot(x,y)返回x,y的點積。

  float circle(in vec2 _st, in float _radius){
    vec2 dist = _st-vec2(0.5);
	  return 1.-smoothstep(_radius-(_radius*0.01),
                         _radius+(_radius*0.01),
                         dot(dist,dist)*4.0);
  }
 
  void main () {
    vec3 color = vec3(circle(v_uv0,0.25));
    gl_FragColor = vec4(color,1.0);
  } 

 

 

 第六個例子 畫長方形

step(闕值A,檢測值B)   B<A返回0.0,B>=A返回1.0。

下面只畫了左和上,left在x<0.1的地方是0,其它地方1;bottom在y<0.1的地方是0,其它地方1。left*bottom有&&的作用,只有x和y都=1結果才是1。

所以只有滿足x>=0.1&&y>=0.1的地方才會是1白色值。

  void main () {
    vec3 color = vec3(0.0);
    float left = step(0.1,v_uv0.x);
    float top = step(0.1,v_uv0.y);
    color = vec3( left * top );
    gl_FragColor = vec4(color,1.0);
  } 

 

下面畫右和下,只有x<=0.9 && y<=0.9的地方是1白色。

  void main () {
    vec3 color2 = vec3(0.0);
    float right = step(0.1,1.0-v_uv0.x);
    float bottom = step(0.1,1.0-v_uv0.y);
    color2 = vec3( right * bottom );
    gl_FragColor = vec4(color2,1.0);
  } 

 

代碼合起來

  void main () {
    vec3 color = vec3(0.0);
    float left = step(0.1,v_uv0.x);
    float top = step(0.1,v_uv0.y);
    color = vec3( left * top );

    vec3 color2 = vec3(0.0);
    float right = step(0.1,1.0-v_uv0.x);
    float bottom = step(0.1,1.0-v_uv0.y);
    color2 = vec3( right * bottom );
    gl_FragColor = vec4(color*color2,1.0);
  } 

 

 第7個例子  從上到下四色漸變

  void main () {
    vec4 o = vec4(1, 1, 1, 1);

    #if USE_TEXTURE
      CCTexture(texture, v_uv0, o);
    #endif

    o *= v_color;

    ALPHA_TEST(o); 

    vec4 color1 = vec4(1,0,0,1);
    vec4 color2 = vec4(0,1,0,1);
    vec4 color3 = vec4(0,0,1,1);
    vec4 color4 = vec4(1,1,1,1);  
    vec4 resultColor = vec4(1,1,1,1);
    if(v_uv0.y < 0.33){
      resultColor = vec4(color1.x + (color2.x - color1.x)*v_uv0.y*3.0, color1.y + (color2.y - color1.y)*v_uv0.y*3.0, color1.z + (color2.z - color1.z)*v_uv0.y*3.0, 1.0);
    }else if(v_uv0.y < 0.66){
      resultColor = vec4(color2.x + (color3.x - color2.x)*(v_uv0.y-0.33)*3.0, color2.y + (color3.y - color2.y)*(v_uv0.y-0.33)*3.0, color2.z + (color3.z - color2.z)*(v_uv0.y-0.33)*3.0, 1.0);
    }else if(v_uv0.y <= 1.0){
      resultColor = vec4(color3.x + (color4.x - color3.x)*(v_uv0.y-0.66)*3.0, color3.y + (color4.y - color3.y)*(v_uv0.y-0.66)*3.0, color3.z + (color4.z - color3.z)*(v_uv0.y-0.66)*3.0, 1.0);
    }
    gl_FragColor = resultColor*o;
  } 

  

 

第8個例子 設置effect屬性

 定義個imgColor屬性

 

定義變量

 

在TestMaterial屬性檢查器中看一看到屬性,並可以選擇顏色

 

 將imgColor賦予圖片

  void main () {
    vec4 o = vec4(1, 1, 1, 1);

    #if USE_TEXTURE
      CCTexture(texture, v_uv0, o);
    #endif

    o *= v_color;

    ALPHA_TEST(o);

    gl_FragColor = o*imgColor;
  }

 

 

第9個例子 攻擊閃白效果

復制cocos的sprite和spine的普通effect

 

 

修改復制的sprite effect,高亮圖片的顏色

 

 

修改復制的spine effect,高亮圖片顏色 

 

 

將修改后的高亮effect賦予圖片,效果是這樣的  

 

 

點擊一個圖片或spine、db時,切換高亮effect持續0.1秒,然后恢復正常的effect,就可以做出攻擊閃白的效果

const { ccclass, property } = cc._decorator;

@ccclass
export default class AttackFlash extends cc.Component {

    @property(dragonBones.ArmatureDisplay)  //龍骨怪物
    monster_db: dragonBones.ArmatureDisplay = null;

    @property(sp.Skeleton)     //spine怪物
    monster_spine: sp.Skeleton = null;

    @property(cc.Sprite)    //普通圖片怪物
    monster_img: cc.Sprite = null;

    @property(cc.Material)  //sprite被攻擊閃白材質
    mat_attacked_sprite: cc.Material = null;

    @property(cc.Material)  //spine被攻擊閃白材質
    mat_attacked_spine: cc.Material = null;

    @property(cc.Material)  //普通材質
    mat_normal: cc.Material = null;

    @property(cc.Material)  //普通spine材質
    mat_normal_spine: cc.Material = null;

    onLoad() {
        this.monster_db.node.on(cc.Node.EventType.TOUCH_END, this.onMonsterDbTap, this);
        this.monster_spine.node.on(cc.Node.EventType.TOUCH_END, this.onMonsterSpineTap, this);
        this.monster_img.node.on(cc.Node.EventType.TOUCH_END, this.onMonsterImgTap, this);
    }

    //點擊dragonBones,切換高亮material
    onMonsterDbTap() {
        this.monster_db.setMaterial(0, this.mat_attacked_sprite);
        this.unschedule(this.flashDb);
        this.schedule(this.flashDb, 0.1);
    }

    //計時結束,切換普通material
    flashDb() {
        this.monster_db.setMaterial(0, this.mat_normal);
    }

    //點擊spine,切換高亮material
    onMonsterSpineTap() {
        this.monster_spine.setMaterial(0, this.mat_attacked_spine);
        this.unschedule(this.flashSpine);
        this.schedule(this.flashSpine, 0.1);
    }

    //計時結束,切換普通material
    flashSpine() {
        this.monster_spine.setMaterial(0, this.mat_normal_spine);
    }

    //點擊圖片,切換高亮material
    onMonsterImgTap() {
        this.monster_img.setMaterial(0, this.mat_attacked_sprite);
        this.unschedule(this.flashImg);
        this.schedule(this.flashImg, 0.1);
    }

    //計時結束,切換普通material
    flashImg() {
        this.monster_img.setMaterial(0, this.mat_normal);
    }
}

  

  

  

  

 

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM