V7投稿|2D描边-外描边

接上一篇2D描边-内描边

WeChat76c095ed74d97cc237faccfaa7edef5c

前言

在上一篇中,我们介绍了内描边的方法,也同时阐述了内描边的优缺点,如果你对那些缺点非常在意,那本篇的外描边也许是你的救星,但同时,你也要承担它的缺点,就是没那么轻度了。话不多说,咱们看看外描边和内描边的天差地别。

原理

既然是外描边,那就不是占用原图的像素画边了,而是在原图外围描上一圈,既然不占用原图像素,那也就不是在它的材质中做文章了,而是要用到后处理。(有关自定义后处理的部分本文章不做介绍,官方以及相关文章很多,我后面给出参考链接)我们要额外有个相机,让它单独照我们需要描边的物体,然后在这个相机照的图上去找边,把边扩大一圈,赋上我们的描边色,再把这些描边单独扣出来,得到一张只有描边的图,再把这个图与主相机的绘制结果做融合,那就得到了你想要的效果,如上图所示,我只描了中间那棵树的边。

描边Camera

所以第一步,就是创建描边Camera,这一步比较简单,我只想让中间那棵树描边,那我就创建个Layer,命名为OUTLINE,赋给那棵树的Layer,然后让描边Camera的Visibility只为OUTLINE,这样它就只照中间那棵树了。同时不要忘了,我们的主相机的Visibility也要加上OUTLINE,不然它就照不到中间的树了。

描边Camera
WeChat1833a22f91843f0e065ae34f8d28a03e

主Camera
WeChat725b3076913c72eeb1b0f6626796fda7

自定义后处理

在Camera只照到描边物体后,我们在自定义后处理前就得到了一张RenderTexture,简称描边rt,那么怎么通过这张rt去描边呢。还记得上一篇吗,我们通过图的Alpha去找边,那时是判断图的周边像素是否有Alpha为0的像素。在这里,我反其道而行之,你想想,这张rt有什么特点,除了树的部分Alpha是大于0的,其余部分Alpha是0,那么我是不是可以把树边缘外面一圈的像素的Alpha也变成大于0呢,这样相当于把树扩大了一圈,然后再把中间树的部分Alpha变成0,是不是就只剩扩大这一圈的部分了,这就是我们想要的东西。

所以核心首先是扩大一圈,我上面说了和上一篇的是反其道而行之,上一篇是找像素周边的最小Alpha,这里呢,就是找像素周边的最大Alpha,你想想,在树外面Alpha为0的部分,如果它周边有一个像素的Alpha不为0,说明什么,说明这个像素挨着树,那它就是我们要的轮廓。

WeChat2bfc24774becf86569841e1b21139af8

如图算法,核心就是这么简单,baseUv是当前像素uv,extendUv是偏移uv,maxAlpha是传进来的最大Alpha,先通过偏移uv得到它周边的这个像素的uv,这里有个新鲜的东西,就是cc_nativeSize,它是系统内置的vec4变量,xy是屏幕的宽高,zw就是宽高的倒数,也就是屏幕rt的纹素,所以我们终于摆脱了上一篇中要传图的纹素的问题。_OutlineSize是描边宽度。_OutlinePPRt是后处理之前的原图,就是我们描边Camera的rt图。得到这个周边像素的Alpha后,和传进来的maxAlpha做比较,返回最大的Alpha,这样我们就能找到周边有Alpha大于0的像素了。

WeChatec6886fda204ca70df86ebd7cea2afdc

这就是遍历当前像素周边8个偏移uv的Alpha的部分,最后得到最大的Alpha。好,现在就已经完成扩一圈的任务了。然后我们要抠去中间树的部分了,这个很简单,我们直接拿扩完的Alpha减去原图的Alpha,就剩外轮廓的Alpha了。

WeChat47a8d349a3db6a4981ec7b3976e33b76

这个aExtend就是扩展Alpha,如果大于0,就是描边,那我们给该像素上描边色,并且Alpha为1,否则就不是,给黑色,Alpha为0。大概如下所示。

WeChat0a5991b1a52d81b8e81eb7aa732daeb8

以上是自定义后处理的shader部分,ts代码部分平平无奇,就是把描边宽度和描边色传给shader就行。

WeChat8965e94e01c309c5898e568a452ec9d0

最后我们把描边Camera的结果输出到一张我们自定义的描边rt中。

融合

在得到了以上的描边rt,我们就要拿它与主Camera做融合了。cocos已经给我们提供了BlitScreen的后处理,只需要提供我们自定义的shader和材质就可以做融合了。

WeChat43a79b973cbe2a1a0a092fe1d37181b6

上面的outline-rt就是我们的描边rt。shader很简单

WeChat1e8678f453860d428b7eb0a79e601b2d

inputTexture是系统传给我们的当前Camera的图,也就是我们主相机照到的所有东西。_BlendTex就是上面的outline-rt,也就是描边rt,我们根据描边rt的Alpha做俩图的融合,没有描边就用原图色,有就用描边色,仅此而已。最后就得到了一开始那张图。

优缺点

说了这么一大通,缺点就是复杂了点。需要自定义后处理。同时也不能不同物体上不同的描边色,除非你多定义几个后处理,这显然对性能不太友好。

但优点明显啊,不论你想描多少边都一次绘制成型,也不用考虑上一篇图集的问题,小窟窿的现象也会少很多。所以看你取舍了。

参考

Cocos Creator 3.8 后期效果 Shader 编写(2/2) 进阶篇

引流

我的前作:2D光影系统,有兴趣可以看看。
2D光影系统-光源一全局方向光
2D光影系统-光源二Sprite光
2D光影系统-阴影

11赞

6666!支持下

本示例工程已上架cocos store,包括内描边与外描边,如有需要请自取,谢谢:2D描边(内描边与外描边)

牛逼牛逼牛

大佬我已经买了您商城里的项目了,我请问一下骨骼动画如何加只外边框呢,我现在每一个骨骼身上都有描边。

这个跟骨骼动画应该没关系,你用来照描边物体的Camera照出的东西是否对,描边物体的layer是否设置全了,这个Camera它照的只是个图,后处理是对这个图做描边处理而已,你检查一下照描边物体的Camera照出的东西对不对吧

1

你没明白我的意思,用鼠标点照这个猫的Camera,Scene窗口会显示这个Camera的图像,看看是不是猫的全图,还是只有一部分

1赞

好!谢谢大佬!

1赞

隐藏主相机然后在激活,会导致主相机无法渲染物体

有一个问题,既然都用rt了,为啥不直接用一个全图sprite+shader?

适配的话 描边会错位 。。

看到了 有个适配的代码

感觉不太对啊,中间镂空的话你这效果不就不对了吗

你用的是内描边还是外描边,我文中提到了镂空的问题,请您仔细看看

大佬我买了这个之后他适配怎么调啊?还有就是我这移植到我自己的项目之后有个报错能帮忙看下吗?加了你在商城留的qq了

这个方法实现的描边一直在最上层。有没有办法实现说是东西覆盖在上面就把他遮住 。。或者有什么思路实现吗?

你好我这边导入到我的项目之后还是有点问题,已经发到邮箱了,方便帮我看一下吗?