Cocos Creator Shader实现镂空遮罩
前言
想不到距离上一遍博客更新已经有13个月之久了,之前定下的一个月至少更新一遍早已不知什么时候抛之脑后。今天开个头,希望以后输出更多的学习总结。
该文起源是同事的一个需求,实现一个简单的镂空遮罩引导,具体效果如下
实现思路
看到这个第一个感觉应该是使用cocos shader,在指定区域做一个镂空处理,需要变化的参数就是指定位置position,镂空尺寸。黑幕渐变的效果就是宽高递减,在ts上根据指定速度递减。
编写shader
下面我们先来实现shader,话不多说,直接上代码
CCEffect %{
techniques:
- passes:
- vert: vs
frag: fs
blendState:
targets:
- blend: true
rasterizerState:
cullMode: none
properties:
size: { value: [1920.0, 1080.0], editor: { tooltip: '节点尺寸' } }
center: { value: [0.5, 0.5], editor: { tooltip: '中心点 (左上角为原点)' } }
width: { value: 0.5, editor: { tooltip: '宽 (目标宽度 / 节点宽度)' } }
height: { value: 0.5, editor: { tooltip: '高 (目标高度 / 节点宽度)' } }
}%
CCProgram vs %{
precision highp float;
#include <cc-global>
in vec3 a_position;
in vec2 a_uv0;
in vec4 a_color;
out vec2 v_uv0;
out vec4 v_color;
void main () {
gl_Position = cc_matViewProj * vec4(a_position, 1);
v_uv0 = a_uv0;
v_color = a_color;
}
}%
CCProgram fs %{
precision highp float;
in vec2 v_uv0;
in vec4 v_color;
uniform sampler2D texture;
uniform Properties {
vec2 center;
vec2 size;
float width;
float height;
};
void main () {
vec4 color = v_color;
color *= texture(texture, v_uv0);
// 边缘
float ratio = size.x / size.y;
float minX = center.x - (width / 2.0);
float maxX = center.x + (width / 2.0);
float minY = center.y - (height * ratio / 2.0);
float maxY = center.y + (height * ratio / 2.0);
if (v_uv0.x >= minX && v_uv0.x <= maxX && v_uv0.y >= minY && v_uv0.y <= maxY) {
// 下面判断4个边,和中间位置
vec2 vertex;
if (v_uv0.x <= minX) {
vertex=vec2(minX,v_uv0.y/ratio);
} else if (v_uv0.x >= maxX) {
vertex=vec2(maxX,v_uv0.y/ratio);
} else if (v_uv0.y <= minY) {
vertex = vec2(v_uv0.x, minY/ ratio); // 上
} else if (v_uv0.y >= maxY) {
vertex = vec2(v_uv0.x,maxY/ ratio); // 下
} else {
discard; // 中间discard表示直接丢弃,不渲染
}
float dis = distance(vec2(v_uv0.x, v_uv0.y / ratio), vertex);
}
color.a *= v_color.a;
gl_FragColor = color;
}
}%
代码很少,就几行,如果不理解的可以翻看我之前几遍shader的文章应该可以懂得。
编写ts更改相应参数
核心shader完成,接下来要做的就是通过获取到这个shader然后通过改变其参数来移动定位到具体镂空位置。这里我这里一个范例,可以直接拿来用。
const {ccclass, property} = cc._decorator;
/**
*
* 把这个类挂在一个遮罩的Sprite里,这个Sprite节点一般是一个黑色的图,透明度直接在color上设置就好
*/
@ccclass
export default class GuideShader extends cc.Component {
/** 这里实现点击某个节点,获取鼠标位置,定位到这里 */
@property(cc.Node)
clickTarget:cc.Node=null;
/** 黑幕遮罩移动的速度 */
@property
public wSpeed:number=90;
@property
public hSpeed:number=70;
private shader:cc.MaterialVariant=null;
/** 镂空的宽高 */
private btnWh:cc.Vec2=cc.v2(160,80);
private isUpdate:boolean=false;
/** 这里一般为屏幕尺寸 */
private widthDiff=1920;
private heightDiff=1080;
onLoad () {
this.node.width=cc.winSize.width;
this.node.height=cc.winSize.height;
}
start () {
this.shader=this.node.getComponent(cc.Sprite).getMaterials()[0];
this.clickTarget && this.clickTarget.on(cc.Node.EventType.TOUCH_END,this.clickHandle,this);
}
private clickHandle(e:cc.Event.EventTouch){
this.setLocation(e.getLocation(),this.btnWh);
}
public setLocation(location:cc.Vec2,focusWH:cc.Vec2)
{
/** 设置相应的shader属性 */
this.shader.setProperty('center',cc.v2(location.x/cc.winSize.width,1-location.y/cc.winSize.height));
this.widthDiff=cc.winSize.width;
this.heightDiff=cc.winSize.height;
this.btnWh=focusWH;
this.isUpdate=true;
}
update (dt) {
if(this.isUpdate)
{
if(this.widthDiff>this.btnWh.x) this.shader.setProperty('width',this.widthDiff/cc.winSize.width);
else this.shader.setProperty('width',this.btnWh.x/cc.winSize.height);
if(this.heightDiff>this.btnWh.y) this.shader.setProperty('height',this.heightDiff/cc.winSize.height);
else this.shader.setProperty('height',this.btnWh.y/cc.winSize.height);
this.widthDiff-=this.wSpeed;
this.heightDiff-=this.hSpeed;
}
}
}
这样就能实现上面gif的效果了。