效果预览
前面的话
参考了《异名》大佬的追光效果,但是他那个只能应用于一张图,但实际游戏开发肯定不止一张图,所以在他的基础上进行了改进,可以做多图的效果,并且可以定义不处理的分组(UI)。
核心思路
根据效果我们需要实现:
- 光圈的形状和大小可控
- 光圈的边缘虚化
- 光圈可操控移动
- 渲染相机渲染结果输出RenderTexture
- 主相机渲染最终结果
实现过程
首先我们放一张跟场景一样大的背景图,假设坐标在中心点,那它的uv坐标就是(0.5, 0.5),我们只需要让圆心到边缘的距离大于指定阈值的像素丢弃(discard)或者透明度为0,是不是就能得到一个圆?
properties:
radius: {value: 0.1}
center: { value: [0.5,0.5]}
fs
void main() {
color.a = step(length(v_uv0 - center), radius);
}
length是取模,step是内置函数:step(a, x) = x >= a? 1 : 0;让alpha非0即1.
上面的代码得出的效果只是一个椭圆,因为uv都是从0到1的,如果应用于正方形的图,那就是一个正圆,但长方形的话,因为横纵比不一样,所以这个图片的横纵比我们可以通过参数传进来:
wh_ratio: { value: 1}
blur: { value: 0.1}
void main() {
float circle = radius * radius;
float rx = center.x * wh_ratio;
float ry = center.y;
float dis = (v_uv0.x * wh_ratio - rx) * (v_uv0.x * wh_ratio - rx) + (v_uv0.y - ry) * (v_uv0.y - ry);
o.a = smoothstep(circle, circle - blur, dis);
}
this.renderSpr.getMaterial(0).setProperty("wh_ratio", cc.winSize.width / cc.winSize.height)
shader新增了wh_ratio横纵比、blur虚化区域,smoothstep相对step来说除了非0即1外,额外多了一个从0到1过渡的变化.
ts代码中将背景图的横纵比传进去,因为我地图是铺满全屏的,所以直接用cc.winSize了,这样得到的效果如下:
最后再将光圈可操作移动加进去:其实就是将位置center实时传进去
toucheEvent(evt: cc.Event.EventTouch) {
let pos = evt.getLocation();
this.bg.getMaterial(0).setProperty('center', [pos.x / cc.winSize.width, (cc.winSize.height - pos.y) / cc.winSize.height]);
}
到这里算是算是把核心逻辑实现了,但是如果你想再挂其他图片进去就会发现其他图片不起作用,这个时候我们就需要用RenderTexture来处理了。
我们新增一张空白图renderSpr,将其尺寸设置成场景一样大,并增加Widget组件使其跟随场景缩放。新增分组render并将renderSpr的分组设置成render,新增RenderCamera,使其只捕获default,而MainCamera只渲染render,底图的材质还原成默认精灵的材质,ts中对底图的操作改成对renderSpr的操作
在ts代码里,我们需要新建一个RenderTexture去获取RenderCamera,并将RenderTexture的纹理实时赋给renderSpr,代码如下
start () {
this.texture1 = new cc.RenderTexture();
this.texture1.initWithSize(cc.winSize.width, cc.winSize.height);
this.renderCamera.targetTexture = this.texture1;
}
update (dt) {
let spriteFrame = new cc.SpriteFrame();
spriteFrame.setTexture(this.texture1);
this.renderSpr.spriteFrame = spriteFrame;
}
不出意外,你能得到这样的一个效果:
图被翻转了,有2种方案解决这个问题:
- renderSpr的scaleY改成-1
- 在顶点中v_uv0 = vec2(a_uv0.x, a_uv0.y);改成v_uv0 = vec2(a_uv0.x, 1.0 - a_uv0.y);
随后在toucheEvent中(cc.winSize.height - pos.y)改成pos.y,使触摸归正。
最后增加动态调整范围和过渡区域的滑动条,其代码如下:
onSliderSize(slider:cc.Slider) {
this.renderSpr.getMaterial(0).setProperty("radius", slider.progress)
}
onSliderBlur(slider:cc.Slider) {
this.renderSpr.getMaterial(0).setProperty("blur", slider.progress * 0.2)
}
运行效果:
其中"追光效果"的文本和底下调节的UI一个受圆圈影响,一个不受圆圈影响,思考一下是怎么做的吧。
完整工程
工程仓库链接: git@gitee.com:onion92/cocos2d-shader-effect.git
最后的话
有兴趣的同学可以关注一下我的公众号。你们的支持是我创作的动力!
感谢各位的观看,希望在渲染的道路上与君共勉,相互成长!