版本:2.4.4
参考:
cocos论坛-creator 2.0 自定义 shader 图片描边效果已测试
目录
一 描边实际效果
二 描边原理
三 创建描边文件.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下载
浙公网安备 33010602011771号