版本:2.4.4

参考:

cocos论坛-creator 2.0 自定义 shader 图片描边效果已测试

cocos论坛-creator用的图片简易描边module

 

目录

一 描边实际效果

二 描边原理

三 创建描边文件.effect和.material

四 使用案例

五 Demo下载

 

一 描边实际效果

 

二 描边原理

采集原图的纹理,将纹理放大并设置为红色,然后将原图和红色合并显示,就形成了描边效果。

 

在OutlineEffect.effect中文件中代码实现为

  void main () {
    vec4 accum = vec4(0.0);
    vec4 normal = vec4(0.0);
     
    normal = texture2D(texture, vec2(v_uv0.x, v_uv0.y));

    accum += texture2D(texture, vec2(v_uv0.x - radius, v_uv0.y - radius));
    accum += texture2D(texture, vec2(v_uv0.x + radius, v_uv0.y - radius));
    accum += texture2D(texture, vec2(v_uv0.x + radius, v_uv0.y + radius));
    accum += texture2D(texture, vec2(v_uv0.x - radius, v_uv0.y + radius));

    accum.r = imgColor.r * accum.a;
    accum.g = imgColor.g * accum.a;
    accum.b = imgColor.b * accum.a;
    accum.a = imgColor.a * accum.a;

    normal = accum * (1.0- normal.a) + normal;

    ALPHA_TEST(normal);
    
    gl_FragColor = normal*v_color;
  }

 

normal就是采集原图texture的纹理数据,所以normal就相当于原图 

normal = texture2D(texture, vec2(v_uv0.x, v_uv0.y));

 

accum是将原图分别往左上,右上,右下,左下移动自定义radius距离后合并的纹理数据,如下面右边的图,相当于4张偏移位置后的图合在了一起。

    accum += texture2D(texture, vec2(v_uv0.x - radius, v_uv0.y - radius));
    accum += texture2D(texture, vec2(v_uv0.x + radius, v_uv0.y - radius));
    accum += texture2D(texture, vec2(v_uv0.x + radius, v_uv0.y + radius));
    accum += texture2D(texture, vec2(v_uv0.x - radius, v_uv0.y + radius));

 

这里是将accum图变成自定义颜色imgColor,例如imgColor为红色,这样就制作了一个比例大于原图radius的红色图

    accum.r = imgColor.r * accum.a;
    accum.g = imgColor.g * accum.a;
    accum.b = imgColor.b * accum.a;
    accum.a = imgColor.a * accum.a;

 

这句就是原图+描边图 = 原图带描边

normal = accum * (1.0- normal.a) + normal;

 

accum*(1.0-normal.a)就是当normal.a=1时,红色描边图就变成(1.0-nomal.a)=(1.0-1.0)=0,意思就是原图不透明的地方,描边图变透明

当normal.a=0时,红色描边图就变成(1.0-normal.a) = (1.0- 0) = 1.0,意思就是原图透明的地方,描边图不透明。

如下图所示,这样就得到了一个红色描边。

 

如果再加上原图的话, normal = accum*(1.0-normal.a) + normal ,那么就是最终描边效果了。

 

三  创建描边effect和material文件

新建effect和material文件,分别命名为OutlineEffect.effect和OutlineMaterial.mtl

 

OutlineMaterial.mtl文件保持默认即可,无需修改

 

OutlineEffect.effect的main函数修改如下,代码将图片边缘变成imgColor的颜色

  void main () {
    vec4 accum = vec4(0.0);
    vec4 normal = vec4(0.0);
     
    //纹理查询,从texture提取指定坐标的颜色信息normal,相当于一张正常的图片,normal就是原图
    normal = texture2D(texture, vec2(v_uv0.x, v_uv0.y));

    //纹理查询,相当于获取了一张比原图比例大radius的图片,accum就是描边图
    accum += texture2D(texture, vec2(v_uv0.x - radius, v_uv0.y - radius));
    accum += texture2D(texture, vec2(v_uv0.x + radius, v_uv0.y - radius));
    accum += texture2D(texture, vec2(v_uv0.x + radius, v_uv0.y + radius));
    accum += texture2D(texture, vec2(v_uv0.x - radius, v_uv0.y + radius));

    //将描边图设置为imgColor色,例如imgColor是红色,则描边图就是红色
    accum.r = imgColor.r * accum.a;
    accum.g = imgColor.g * accum.a;
    accum.b = imgColor.b * accum.a;
    accum.a = imgColor.a * accum.a;

    //accum * (1.0- normal.a)相当于描边图和原图重叠的地方都为透明,这样只剩下描边
    //normal 还是原图
    //accum * (1.0- normal.a) + normal 就是描边+原图了
    normal = accum * (1.0- normal.a) + normal;

    ALPHA_TEST(normal);
    
    gl_FragColor = normal*v_color;
  }

  

OutlineEffect.effect开头处的CCEffect内自定义颜色imgColor和自定义半径radius

CCEffect %{
  techniques:
  - passes:
    - vert: vs
      frag: fs
      blendState:
        targets:
        - blend: true
      rasterizerState:
        cullMode: none
      properties:
        texture: { value: white }
        alphaThreshold: { value: 0.5 }
        imgColor: {value: [255,0,255,255] ,editor: {type: color} }
        radius: { value: 0.01 }
}%

 

在main上方声明imgColor和radius两个变量,如下图种uniform的代码

  uniform color{
    vec4 imgColor;
  };

  uniform Properties {
    float radius;
  };
 
  void main () {
    vec4 accum = vec4(0.0);
    vec4 normal = vec4(0.0);

    ....
    ....
    ....

  

选择OutlineMaterial.mtl文件,在属性中设置Effect为OutlineEffect,属性面板中imgColor颜色可自行定义,也可在代码中动态修改

若想颜色初始默认就是本来颜色,则将imgColor的rgb、alpha属性全部选成0即可

radius是描边半径大小

 

OutlineEffect.effect全文如下:

// Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.  

CCEffect %{
  techniques:
  - passes:
    - vert: vs
      frag: fs
      blendState:
        targets:
        - blend: true
      rasterizerState:
        cullMode: none
      properties:
        texture: { value: white }
        alphaThreshold: { value: 0.5 }
        imgColor: {value: [255,0,255,255] ,editor: {type: color} }
        radius: { value: 0.01 }
}%


CCProgram vs %{
  precision highp float;

  #include <cc-global>
  #include <cc-local>

  in vec3 a_position;
  in vec4 a_color;
  out vec4 v_color;

  #if USE_TEXTURE
  in vec2 a_uv0;
  out vec2 v_uv0;
  #endif

  void main () {
    vec4 pos = vec4(a_position, 1);

    #if CC_USE_MODEL
    pos = cc_matViewProj * cc_matWorld * pos;
    #else
    pos = cc_matViewProj * pos;
    #endif

    #if USE_TEXTURE
    v_uv0 = a_uv0;
    #endif

    v_color = a_color;

    gl_Position = pos;
  }
}%


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

  in vec4 v_color;

  #if USE_TEXTURE
  in vec2 v_uv0;
  uniform sampler2D texture;
  #endif

  uniform color{
    vec4 imgColor;
  };

  uniform Properties {
    float radius;
  };
 
  void main () {
    vec4 accum = vec4(0.0);
    vec4 normal = vec4(0.0);
     
    //纹理查询,从texture提取指定坐标的颜色信息normal,相当于一张正常的图片,normal就是原图
    normal = texture2D(texture, vec2(v_uv0.x, v_uv0.y));

    //纹理查询,相当于获取了一张比原图比例大radius的图片,accum就是描边图
    accum += texture2D(texture, vec2(v_uv0.x - radius, v_uv0.y - radius));
    accum += texture2D(texture, vec2(v_uv0.x + radius, v_uv0.y - radius));
    accum += texture2D(texture, vec2(v_uv0.x + radius, v_uv0.y + radius));
    accum += texture2D(texture, vec2(v_uv0.x - radius, v_uv0.y + radius));

    //将描边图设置为imgColor色,例如imgColor是红色,则描边图就是红色
    accum.r = imgColor.r * accum.a;
    accum.g = imgColor.g * accum.a;
    accum.b = imgColor.b * accum.a;
    accum.a = imgColor.a * accum.a;

    //accum * (1.0- normal.a)相当于描边图和原图重叠的地方都为透明,这样只剩下描边
    //normal 还是原图
    //accum * (1.0- normal.a) + normal 就是描边+原图了
    normal = accum * (1.0- normal.a) + normal;

    ALPHA_TEST(normal);
    
    gl_FragColor = normal*v_color;
  }
}%

 

四 实际使用  

将场景中放两张图片,第一张是原图用于对比,第二张将属性面板中材质Materials属性设置为OutlineMaterial。

 

 

代码中获取带有outlinematerial的图片,并动态赋值材质imgColor属性和radius属性,即可改变描边颜色和大小。

动态设置描边颜色代码全文如下:

const { ccclass, property } = cc._decorator;

@ccclass
export default class OutlineDemo extends cc.Component {

    @property({ type: cc.Sprite, tooltip: "材质为OutlineMaterial图片" })
    outlineSprite: cc.Sprite = null;

    onLoad() {
        //获取图片的材质
        let material: cc.Material = this.outlineSprite.getMaterial(0);
        //打印材质的pass属性
        console.log((material as any)._effect._passes[0]._defines["USE_TEXTURE"]);
        //监听鼠标移动
        this.node.on(cc.Node.EventType.TOUCH_MOVE, (e: cc.Event.EventTouch) => {
            let localPos = this.outlineSprite.node.parent.convertToNodeSpaceAR(e.getLocation());
            //判断鼠标移入图片内,则设置颜色为红色
            if (this.outlineSprite.node.getBoundingBox().contains(localPos)) {
                material.setProperty("imgColor", new cc.Vec4(255, 0, 0, 255));
                material.setProperty("radius", 0.002);
                //判断鼠标移出图片,则设置颜色为0,还原成本来颜色
            } else {
                material.setProperty("imgColor", new cc.Vec4(0, 0, 0, 0));
            }
        }, this);
    }
}

  

 五 Demo下载

 demo下载

 

posted on 2022-08-11 23:29  gamedaybyday  阅读(4528)  评论(4编辑  收藏  举报