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图形学计算中的相关计算,由于其可编辑性,可以实现各种各样的图像效果而不用受显卡的固定渲染管线限制。

 

二  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);
    }
}

  

  

  

  

 

 

  

 

posted on 2021-07-14 14:12  gamedaybyday  阅读(4122)  评论(0编辑  收藏  举报