zoukankan      html  css  js  c++  java
  • 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);
        }
    }
    

      

      

      

      

      

  • 相关阅读:
    HDU--2024
    HDU--2021
    HDU--2020
    HDU--2019
    HDU--2018
    HDU--2017
    HDU--2016
    python全栈开发day13-迭代器、生成器、列表推导式等
    python全栈开发day12-函数的有用信息、带参数的装饰器、多个装饰器装饰一个函数、global和nonlocal的进一步解析和总结
    Python3使用Print输出带颜色字体
  • 原文地址:https://www.cnblogs.com/gamedaybyday/p/15010550.html
Copyright © 2011-2022 走看看