OpenGL 實現視頻編輯中的轉場效果

轉場介紹

阿里P6P7【安卓】進階資料分享+加薪跳槽必備面試題
轉場效果是什麼?面試

轉場效果,簡單來講就是兩段視頻之間的銜接過渡效果。函數

如今拍攝 vlog 的玩家愈來愈多,要是視頻沒有一兩個炫酷的轉場效果,都很差意思拿出來炫酷了。post

那麼如何在視頻編輯軟件中實現轉場效果呢?學習

這裏提供使用 OpenGL 實現視頻轉場的一個小示例,咱們能夠經過自定義 GLSL 來實現不一樣的轉場效果。動畫

以在 Android 平臺上做爲演示,但其實不論是 Android 仍是 iOS,實現的原理都是同樣的。spa

首先要有兩段視頻,視頻 A 和視頻 B,先播放視頻 A 後播放視頻 B,中間有一段過程稱爲 C ,C 就是視頻 A、B 作轉場動畫的時間段。code

以下所示:orm

播放器按照時間順序,從 A -> C -> B 的播放,這樣就有了轉場的效果。視頻


視頻轉場,首先就得有視頻,直接從視頻 A、B 中解碼出當前幀並經過 OpenGL 顯示到屏幕上就行了,若是你對這個操做不熟悉的話,能夠查看個人公衆號【紙上淺談】歷史文章,都有寫過相關內容。圖片

這裏以圖片來替代視頻 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] 那麼只能採樣獲得一半內容了。

轉場效果實現

混合函數 mix

因爲轉場效果是須要視頻 A 和視頻 B 進行疊加混合的,而 GLSL 內嵌了 mix 函數進行調用。

對於 GLSL 中有哪些內嵌的函數能夠直接調用的,能夠參考寫過的文章記錄:

OpenGL ES 2.0 着色器語言 GLSL 學習 https://glumes.com/post/openg...

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 的前一段時間的視頻作轉場效果。

所以也能夠對轉場效果作個分類:

  • 視頻 A 最後一幀與視頻 B 第一幀作轉場動畫
  • 視頻 A 最後一幀與視頻 B 前一段時間視頻作轉場動畫
  • 視頻 A 最後一段時間視頻 與視頻 B 第一幀作轉場動畫
  • 視頻 A 最後一段時間視頻 與視頻 B 前一段時間視頻作轉場動畫

這四個分類的實現原理其實都差很少,若是是一段視頻的話,那麼就在視頻播放時更新對應紋理。

以上就在關於使用 OpenGL 在視頻編輯中實現轉場效果的講解,經過這篇文章但願你們能夠掌握轉場的基本實現原理。
阿里P6P7【安卓】進階資料分享+加薪跳槽必備面試題

相關文章
相關標籤/搜索