当下最火的短视频应用莫过于抖音了,凭借着丰富的视频特效,抖音吸引了很多年轻用户的青睐,今天我们来看一下,抖音特效如何在 Web 端实现。
核心原理
实现原理比较简单,总结起来有如下三个步骤:
1、使用预渲染 canvas 绘制 video 的每一帧画面。
2、将预渲染 canvas 作为纹理传到显存中。
3、着色器程序对纹理进行后期处理。
步骤看似简单,但是第二、三步骤牵扯到一些 WebGL 和着色器的相关知识,所以大家看不懂的地方不要着急,可以稍微补充一下相关知识。
主要实现
每一个特效都需要 JS 程序和着色器程序的支持,我会分别从 JS 程序和着色器程序两方面讲解。
下面的特效旨在演示着色器程序的能力和魅力,所以特效主要在着色器端实现,那么JS 程序做了哪些事情呢?
JS 主要是实现读取视频的每一帧画面,渲染到一个临时 Canvas 中,并将临时 Canvas 作为纹理传到显存中,以下特效的 JS 部分基本一样,所以我们先看下 JS 逻辑。
1、首先看下 html 的结构
复制代码
我们放置了两个 Canvas ,其中第一个 Canvas 用来展示特效,第二个 Canvas 用来绘制视频帧画面,比较简单。
2、接着看下 JS 逻辑的核心代码
读取视频帧画面,将其渲染到临时 Canvas 上,并将临时 Canvas 作为纹理传送到显存中。
ctxBg.drawImage(video, 0, 0, 256, 256);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvasBg);
gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.uniform1i(u_Texture, 0);
复制代码
启动一个定时器,每次传送一个时间变量到显存。
function loop() {
if (video.ended) {
return;
}
computeFrame();
time = time + interval;
gl.uniform1f(u_Time, time);
// 清除画布
gl.clear(gl.COLOR_BUFFER_BIT);
if (positions.length <= 0) {
return;
}
//绘制图元设置为三角形。
var primitiveType = gl.TRIANGLES;
//因为我们要绘制6个点,所以执行6次顶点绘制操作。
gl.drawArrays(gl.TRIANGLES, 0, positions.length / 3);
timer = requestAnimationFrame(loop);
}
复制代码
3、真正实现特效的部分是在着色器程序上,当然着色器源码也是从 JS 中传到显存中,这部分代码我们按下不表,只带大家感受一下各个特效。
图像有些虚,大家可能看不太清楚,不过可以去示例网页上去感受一下~
缩放
闪白
残影
灵魂出窍
毛刺
结语
以上示例的代码实现中包含一些 WebGL 的基本操作,比较冗长,想看示例的同学可以移步到我的 github去查看。