导言: 目前动画效果越来越酷炫,动画的绘制难度也逐渐增大,本期我们讲下透明视频,动画的另一种方案, 本文通过webgl来实现透明动画视频。 最终效果
demo
(如果没反应,点一下屏幕)
需求是啥?
比如我们出要实现一个动画效果, 要怎么实现呢?
这里我们的核心目标是动画的背景可替换,比如说,这里的视频可以有背景色,像以下的图。
目前是不是想到了各种lottie动画库,或直接用一个gif动画来代替?
接下来我们先对方案进行对比。
这里我们的直接目的是实现一个透明背景的动画,目前有几种方案,包括gif、png或者对视频进行转换来实现
选用webgl的话,目前已经有很多很成熟且大型的库可供使用比如
pixijs
lottie
,这些库动画效果酷炫,体积偏大,比如
pixijs
的cdn版本有80k,
lottie
中的
lottie-web
文件就更大了,gzip压缩之后还有92k。
鉴于我们只是做一个简单的动画效果,我们简单学一下webgl,来实现该功能吧。webgl入门可以看我之前的一篇入门篇,
从0基础到写一个简易滤镜
,这样我们就可以来实现一个透明视频的动画了。
我们想下这里用webgl如何进行绘制,怎么能从一个视频中抠出其中的非背景色部分?难道要使用到抠图技术了吗?目前感觉有点难度,并且细节之处还担心有可能有残留阴影。那么,怎么办呢?
我们把问题一步一步转化。
接下来,我们的问题就是如何把图片的背景变成透明
问题再次拆分
如何确认哪块属于背景
如何设置背景透明度
首先我们解答第一个问题,左边是遮罩,这样我们就能知道哪块区域是背景区域,
其次,设置背景透明度,即叠加两者颜色后,对rgba中的alpha进行处理即可。
我们核心的webgl算法已经实现
// 片元着色器, gl_FragColor 即为每像素的颜色
const vShader = `
precison lowp float
varying vec2 v_texcoord
uniform sample2D u_sample
void main(void) {
gl_FragColor = vec4(texture2D(u_sampler, v_texcoord).rgb, texture2D(u_sampler, v_texcoord+vec(-0.5, 0)).r)
这里如果看不懂的话,可以先看看之前的一篇文章webgl从0到写一个简易滤镜功能。
回归问题,目前来看,我们的问题 如何将图片的背景设置为透明 已经解决了,那么,如何将视频的背景设置为透明呢? 抽象来看,视频就是一帧一帧的图片,因此,通过requestFrame
把一帧一帧的带有遮罩和真实图片进行混合处理,就可以达到我们想要的效果了。
按着这个思路,我们就可以开始进行处理了
我们看看 效果 (如果没反应,点一下屏幕)
我们看一下,这里会出现图像模糊的效果,我们开始思考,这个是不是webgl的问题?是不是webgl绘制才有这种问题?其他的绘制方式会不会有同样的问题?
我们首先需要确认是不是只有webgl才有这个问题,与webgl相对的是canvas。
因此,我们写一个canvas来看看。发现canvas有同样的问题。
因此,排除webgl本身的问题。
绘制方面出现问题的话,我们可以考虑到,是不是分辨率和设备像素比导致的问题,在网上搜了一下,果然是这个原因 解决 canvas 在高清屏中绘制模糊的问题,不过文中的提到backingStorePixelRatio
这个属性我在webgl
和canvas
上下文中都试了几下,都没有获取到值,因此把这个当作是1来进行处理。
到此,我们通过设置webgl绘制的设备像素比,进行缩放行为,看看能否消除该不良效果
this.radio = window.devicePixelRatio;
gl.viewport(
this.options.width * this.radio,
this.options.height * this.radio
效果完美😄!
给大家看看demo (如果没反应,点一下屏幕)
完整代码 ->
这里我们可以看看效果,同样的效果,在我这台破mac下,通过canvas绘制后,CPU占用达80+,改成webgl后,降到20+。如果换成gif实现,试了一下,体积变为原体积的3倍。
由此得出我们的结论,webgl绘制,效果perfect😄!