前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何在WebGL中实现短视频卡点动效?

如何在WebGL中实现短视频卡点动效?

作者头像
腾讯云开发者
发布2021-01-28 10:10:15
7510
发布2021-01-28 10:10:15
举报

导语 | “腾讯微剪”是一个小程序端的实时预览短视频编辑插件,支持丰富的视频效果,近期上架了视频模板的功能,本文将针对其中的卡点模板切入动效,还原技术实现的思路,希望与大家一同交流。文章作者:杨琳,腾讯前端研发工程师。

卡点动效展示

一、效果分解

慢动作分解一下上面的视频效果,可以看到图片入场沿着从左上角至中心点的曲线位移,加上一个运动模糊来模拟快速进入然后减速的效果,同时会有一个弹性效果。

二、沿贝塞尔曲线移动

通过分解可以看到图片进入显示区域的轨迹是一条类似如下图这样的曲线:

在数学中可以使用三次贝塞尔曲线来表达这样的曲线,三次贝塞尔曲线的公式如下:

类似的曲线还有圆弧线,但是贝塞尔曲线更灵活通用,且x坐标刚好适配在[0,1]这个区间之间。

确定曲线的端点,这里的坐标系y轴和WebGL坐标系y轴方向相反,因此记得对y做一下换算。

得到WebGL坐标系中四个控制点:p0 = vec2(0.4,0.2), p1 = vec2(0.5,0.303), p2 = vec2(0.5,0.362), p3 = vec2(0.5,0.5)。

Shader中增加Bezier曲线的公式:

float Bezier(float p0, float p1, float p2, float p3, float t) {  float x0;  float x1;  float x2;  float x3;  x0 = p0 * pow((1. - t), 3.);  x1 = 3. * p1 * t * pow((1. - t), 2.);  x2 = 3. * p2 * pow(t, 2.) * (1. - t);  x3 = p3 * pow(t, 3.);
  return x0 + x1 + x2 + x3;}vec2 getBezierPoint(vec2 p0, vec2 p1, vec2 p2, vec2 p3, float progress) {  return vec2(    Bezier(p0.x, p1.x, p2.x, p3.x, progress),    Bezier(p0.y, p1.y, p2.y, p3.y, progress)  );}

根据当前的动画进度拿到图片的当前的位置,计算出位移,并从图片上取到对应点:

uniform float progress;uniform sampler2D inputImageTexture;...vec4 getColor(vec2 position) {    // scalarRatio为图片缩放比例    position = (position - vec2(0.5, 0.5)) / scalarRatio + vec2(0.5, 0.5);    return texture2D(inputImageTexture, position);}void main() {...    vec2 p0 = vec2(0.45, 0.2);    vec2 p1 = vec2(0.5,0.303);    vec2 p2 = vec2(0.5,0.5);    vec2 p3 = vec2(0.5, 0.5);

    vec2 currentPos = getBezierPoint(p0, p1, p2, p3, progress);        vec2 distance = currentPos - vec2(0.5, 0.5);    // 根据distance做图片平移操作    vec2 pos = textureCoordinate.xy - distance;    gl_FragColor = getColor(pos);}

这时图片可以动起来了,但是效果比较呆板,始终都是朝着一个方向。

为了模拟沿着曲线滑动的效果,我们沿着贝塞尔曲线的切线再给它加上一个旋转。贝塞尔曲线求切线方向向量的方法如下:

vec2 computeBezierDerivative(vec2 p0, vec2 p1, vec2 p2, vec2 p3, float progress) {  p0 = 3.0 * (p1-p0);  p1 = 3.0 * (p2-p1);  p2 = 3.0 * (p3-p2);  return p0 * (1. - progress) * (1. - progress) + 2. * p1 * (1. - progress) * progress + p2 * progress * progress;}

在位移的同时计算一个旋转量:

vec2 getRotate(vec2 pos,float angle){    float s=sin(angle);    float c=cos(angle);    vec2 center=vec2(.5,.5);    // 此处省略了图片适配canvas比例的操作    ...    mat2 rotMat=mat2(c,-s,s,c);        pos=pos-center;    pos=rotMat * pos;    pos=pos+center;    return pos;}
void main() {...    vec2 p0 = vec2(0.4, 0.2);    vec2 p3 = vec2(0.5, 0.5);    vec2 p1 = vec2(0.5, 0.4);    vec2 p2 = vec2(0.5, 0.45);
    vec2 currentPos = getBezierPoint(p0, p1, p2, p3, progress);    vec2 distance = currentPos - vec2(0.5, 0.5);    // 根据distance做图片平移操作    vec2 pos = textureCoordinate.xy - distance;        vec2 dir = computeBezierDerivative(p0, p1, p2, p3, progress);    angle = asin(dir.x / dir.y);    pos = getRotate(pos, -angle);        gl_FragColor = getColor(pos);}

下图所示为施加后的效果,此时可以看到图片沿着贝塞尔曲线移动的效果更加自然了。

但是匀速移动往往看起来比较呆板没有动感,常常用到缓动曲线来使运动更加有节奏,查看更多常用的缓动函数可点击下方传送门【1】。

【1】https://easings.net/#

这里我使用的是弹性曲线,来实现一种duang~duang~的感觉,处理逻辑如下:

float easeOutElastic(float progress){    float c4 = (2. * 3.1415926) / 3.;    return progress == 0.        ? 0.        : progress == 1.        ? 1.        : pow(2., -10. * progress) * sin((progress * 10. - 0.75) * c4) + 1.;}

再用easeOutElastic函数处理一下progress:

vec2 currentPos = getBezierPoint(p0, p1, p2, p3, easeOutElastic(progress));

三、动态模糊实现

模糊算法是非常常用的图像处理算法之一了。常见的有高斯模糊、径向模糊、旋转模糊、运动模糊等,模糊可以在视觉上更好地造成快速运动的感觉。

在放大缩小效果中常常用到径向模糊,平移的时候则常用到运动模糊,旋转模糊顾名思义就常用在旋转的场景中了。

在示例斜切入画的效果里,图片主要是沿着曲线在走向下,因此我们给它加一个竖向的运动模糊。

// 动态模糊算法vec4 motionBlur(vec2 velocity, vec2 position) {    int kernelSize = 20;    float offset = 0.;    int MAX_KERNEL_SIZE = 2048;    vec4 color = getColor(position, index);    if (kernelSize == 0)    {        return color;    }    velocity = velocity / filterArea.xy;    offset = -offset / length(velocity) - 0.5;    int k = kernelSize - 1;    for(int i = 0; i < MAX_KERNEL_SIZE - 1; i++) {        if (i == k) {            break;        }        vec2 bias = velocity * (float(i) / float(k) + offset);        color += getColor(position + bias);    }    return color / float(kernelSize);}void main() {    ...    float vy = 40. * progress;    vec4 color = motionBlur(vec2(0., vy), pos);}大功告成,最终效果如下图所示:

四、结语

本文主要使用贝塞尔曲线位移+旋转+缓动曲线实现了一个照片的动态效果,加上动感的音乐就可以组合成时尚的卡点视频。同样的思路还可以实现更多的效果,比如我们经常在各种小视频上看到的“甩来甩去”的效果。

如下图所示,这里就使用了一个横向的贝塞尔曲线,加上沿贝塞尔曲线的法向量旋转,前文已经给出了求切线方向的方法,求法向量也就很简单了。

最后再来给大家安利一波腾讯微剪,腾讯微剪是一个短视频剪辑小程序插件,支持实时编辑预览,支持多视频图片的导入导出,内置精美的滤镜、特效、贴纸、字体,自带炫酷模板,接入简单,适合各种音视频剪辑的场景,欢迎扫码体验~

文章推荐

hash+跳表,玩转Redis有序集合

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-01-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 腾讯云开发者 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • hash+跳表,玩转Redis有序集合
相关产品与服务
云开发 CloudBase
云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档