转场效果是什么?java
转场效果,简单来讲就是两段视频之间的衔接过渡效果。微信
如今拍摄 vlog 的玩家愈来愈多,要是视频没有一两个炫酷的转场效果,都很差意思拿出来炫酷了。函数
那么如何在视频编辑软件中实现转场效果呢?源码分析
这里提供使用 OpenGL 实现视频转场的一个小示例,咱们能够经过自定义 GLSL 来实现不一样的转场效果。post
以在 Android 平台上做为演示,但其实不论是 Android 仍是 iOS,实现的原理都是同样的。学习
首先要有两段视频,视频 A 和视频 B,先播放视频 A 后播放视频 B,中间有一段过程称为 C ,C 就是视频 A、B 作转场动画的时间段。动画
以下所示:spa
播放器按照时间顺序,从 A -> C -> B 的播放,这样就有了转场的效果。3d
视频转场,首先就得有视频,直接从视频 A、B 中解码出当前帧并经过 OpenGL 显示到屏幕上就行了,若是你对这个操做不熟悉的话,能够查看个人公众号【纸上浅谈】历史文章,都有写过相关内容。code
这里以图片来替代视频 A、B 中解码出来的帧。
最终效果以下:
模拟 fps 为 30 的视频,用 RxJava 每间隔 30 ms 就触发一次 OpenGL 渲染。
Observable .interval(30, TimeUnit.MILLISECONDS) .subscribeOn(AndroidSchedulers.mainThread()) .subscribe { mGLSurfaceView.requestRender() }
另外,若是在视频 A 播放阶段不断地改变图片,也就是更新纹理内容,就至关于在真实的解码视频进行播放了。
固然这些操做只是为了让这个小例子更加贴近真正的视频转场,重要的仍是在于如何实现转场的 Shader 效果。
首先转场的时候要有两个纹理做为输入,那么确定要定义两个 sampler2D
进行采样了。
varying vec2 vTextureCoord;//接收从顶点着色器过来的参数 uniform sampler2D sTexture1; uniform sampler2D sTexture2;
其中 sTexture1
对应于视频 A 内容,sTexture2
对应于视频 B 内容。
vTextureCoord
对应于顶点着色器传递过来的纹理坐标,视频 A 和 视频 B 都须要用到这个纹理坐标。
这个时候,只要调用 texture2D 方法就能获得视频 A 和 视频 B 的内容了。
// 获得视频 A 的内容 texture2D(sTexture1,vTextureCoord) // 获得视频 B 的内容 texture2D(sTexture2,vTextureCoord)
要注意的是这里说获得视频 A/B 的内容,是获得纹理坐标对应的图像内容。也就是说若是纹理坐标是 [0,1] 范围内,那么能够获得视频 A/B 的所有图像内容。若是坐标是 [-0.5,0.5] 那么只能采样获得一半内容了。
因为转场效果是须要视频 A 和视频 B 进行叠加混合的,而 GLSL 内嵌了 mix
函数进行调用。
对于 GLSL 中有哪些内嵌的函数能够直接调用的,能够参考写过的文章记录:
OpenGL ES 2.0 着色器语言 GLSL 学习
mix
函数的声明以下:
genType mix(genType x,genType y,float a) // 其中 genType 泛指 GLSL 中的类型定义
它的主要功能是使用因子 a 对 x 与 y 执行线性混合,既返回 x * (1-a) + y * a
。
如今,经过 texture2D 能获得视频帧内容,经过 mix 能进行视频帧混合叠加,那么就能够获得最后转场视频帧了。
vec4 color = mix(texture2D(sTexture1,vTextureCoord),texture2D(sTexture2,vTextureCoord),a);
彷佛到这里就能够大功告成了,实际上才刚刚完成了一半~~~
要知道转场效果是随着时间来播放的,就上面的例子中,转场时间内,一开始都是视频 A 的内容,而后视频 A 逐渐减小,视频 B 逐渐增多,到最后全是视频 B 内容,在咱们的 Shader 中也要体现这个时间变化的概念。
在 Shader 中定义 progress
变量,表明转场的播放进度,进度为 0 ~ 1.0 之间。
uniform float progress;
同时在每一次渲染时更新 progress
变量的值。
GLES20.glUniform1f(muProgressHandle, mProgress);
当 progress
为 0 时表明转场刚刚开始,当 progress
为 1 时表明转场已经结束了。
if (mProgress >= 1.0f) { mProgress = 0.0f; } else { mProgress += 0.01; }
这里 progress
每次递增 0.01,完成一次转场就须要 100 次渲染,每次渲染间隔 30ms,那么一次转场动画就是 3000ms 了,固然这个能够本身调节的。
再回到 mix
函数的参数 a
,这个参数起到了随时间调节转场混合程度的做用。当 a = 0 时,全是视频 A 的内容, 当 a = 1 时,全是视频 B 的内容。
如上图所示,在转场动画的某一帧,左侧是视频 A 的内容,由于此时 a = 0,右侧是视频 B 的内容,此时 a = 1 。
能够看到在一次渲染绘制内 a 既要能等于 0 ,还要能等于 1 ,这个是怎么实现的呢?
事实上咱们说的一次渲染绘制,一般指 OpenGL draw 方法的一次调用,可是在这一次调用里,仍是有不少步骤要执行的。
OpenGL 渲染管线会先执行顶点着色器,而后光栅化,再接着就是片断着色器,片断着色器会根据纹理坐标采样纹理贴图上的像素内容进行着色,所以片断着色器在管线中会屡次执行,针对每一个像素都要进行着色。
上面图像的小方块就比如一个像素,每一个像素都要执行一个片断着色器。
首先,确定全部的像素都要进行着色的。左侧方块采样视频 A 的纹理进行着色,右侧方块采样视频 B 的纹理进行着色。
回到以下代码:
mix(texture2D(sTexture1,vTextureCoord),texture2D(sTexture2,vTextureCoord),a);
只要保证绘制左侧时 a = 0,绘制右侧时 a = 1 就好了。这里能够经过移动纹理坐标来控制 a 的值。
vec2 p = vTextureCoord + progress * sign(direction); float a = step(0.0,p.y) * step(p.y,1.0) * step(0.0,p.x) * step(p.x,1.0);
OpenGL 中定义纹理坐标范围是 [0 ~ 1] ,能够将范围右移 0.5 ,从而变成 [0.5 ~ 1.5] ,此时纹理坐标一半位于规定范围内,一半超出界外了。
这样就能够经过对当前像素小方格对应的纹理坐标的 x,y 值运用 step
函数进行判断是否在界内,就能够决定是采样视频 A 仍是视频 B 的图像了。
当每次刷新 progress 时,就向右移一小段距离,视频 A 随着右移而变少,视频 B 变多,这样就是实现了转场效果。
不知道这个简单的例子有没有让你想到些什么?
对的,没错,就是升职加薪,走向巅峰必备的 PPT 技能,这种视频转场的实现效果就和咱们在编辑 PPT 动画时添加的同样。
并且这仍是比较简单的,想要作一些花里胡哨的转场特效,缺乏灵感就能够参考 PPT 里面的动画了。
另外,咱们还能够对转场效果作一些总结分类,好比示例中用的是图片,能够理解成视频 A 的最后一帧显示与视频 B 的第一帧显示作转场效果,这种转场效果实际使用的人比较少,大多数是视频 A 的最后一帧与视频 B 的前一段时间的视频作转场效果。
所以也能够对转场效果作个分类:
这四个分类的实现原理其实都差很少,若是是一段视频的话,那么就在视频播放时更新对应纹理。
以上就在关于使用 OpenGL 在视频编辑中实现转场效果的讲解,经过这篇文章但愿你们能够掌握转场的基本实现原理。
文中用到的代码示例,能够关注个人微信公众号【纸上浅谈】,回复 “转场” 便可~~~
技术交流,欢迎加微信好友~~
欢迎关注微信公众号【纸上浅谈】,看更多音视频、OpenGL、多媒体开发文章