Quantcast
Channel: AJ Hsu
Viewing all articles
Browse latest Browse all 8

[HLSL] 在WPF中實現透明影片播放技術 (Playing Video with Alpha Channel in WPF)

$
0
0

image

在最近的工作中,需要實現透明影片疊加到畫面上的需求(如上圖),就像是openFramewoks中,可以讀取帶有透明色版(Alpha Channel)的mov檔,覆蓋在原有的畫面上。

所以我就來測試了一下WPF中是否能支援透明影片的功能,而結果當然是你我心裡都想著的那個共同答案──沒有。

 

Goal

一般來說,在WPF中若要做到影片的播放,都會選用WPF內建的MediaElement元件來達成播放;

而MediaElement所支援的影片格式,大致上就是Windows Media Player所支援的格式,如WMV、AVI等Windows內建格式。

但像WMV及AVI這兩種格式,均不支援透明度的渲染,也就是影片中無法夾帶Alpha Channel。

經過了一番研究之後,我暫時列出以下方式,可以間接達成透明影片的效果:

 

PNG Sequence

既然原始的影片格式無法支援透明度,那我們就從能夠支援透明度的PNG格式下手,把原本的透明影片Render成一張一張的PNG,然後再透過特製的Class去frame by frame的播放它,其實就間接達到了透明影片的播放效果,這個方法在網路上稱之為PNG Sequence。

但是當我們採用如PNG Sequence這種間接完成透明影片的效果時,會導致其他更多的問題發生,如播放速率不穩定、記憶體占用龐大、無法支援音訊等間接問題產生。

雖然說PNG Sequence能帶來最好的畫質及透明支援,但在流暢度及易用性上並不會是首選;因此,如何在WPF中實現透明影片便是一個在網路上還無法解決的問題之一。

image

(圖:PNG Sequence需要讀取大量的PNG)

 

Color Keying

image

這是一種透過影像處理的方式,將影片中的指定顏色Key掉。例如將綠幕影片中的綠色,Key成透明底色,藉此達到透明影片效果。

但這個方法有諸多麻煩,比如:

1. 影片製作時期需要特別加入綠色當作透明色

2. 若綠色是藉由現實方式拍攝(如綠幕),則綠色會有漸層範圍,也就是需要去除的綠色不會只有一組(R0,G255,B0),而是一個區間(R0~50,G200~255,B0~50),增加去除的難度以及失敗率。

image

(圖:若去除的區間不夠廣的時候,則會有綠色殘留)

3. 髮絲或者物體邊緣,會出現綠色的反射光澤,而這些邊緣的顏色通常都會與物體顏色混在一起,幾乎不在去除的綠色範圍中。若過度擴大去除範圍,則會造成影像殘缺。

image

(圖:若去除的區間太廣,則會造成影像殘缺)

4. 無法達成柔化的透明邊緣。也就是Pixel要嘛就完全透明,要嘛就完全不透明,結果造成強烈的鋸齒邊緣。若要解決此問題就需要另外將Mask加上Blur運算,大大降低運算效率(圖片模糊是一項非常消耗資源的演算法)。

5. 若要克服上述的綠幕問題,幾乎是可以另外寫一篇文章來探討綠幕去背的技術了

(實際範例可參考這篇)

 

Pixel Shader

在思考如何在C#中可以完成高效率的透明影片支援,我決定從Pixel Shader出發!原因其一是Pixel Shader為像素低階運算,同時對影像處理的支援度極高。其二是HLSL採用GPU運算,可以與其他程式邏輯需求分開,降低透明影片所占用的CPU運算資源。

 

Masking RGB Channel Video w/ Alpha Channel Video

因此我想到的第一個方式,是將Alpha Channel獨立渲染,並將其作為RGB影片之Mask來源。

 

實際作法如下:

1. 將MOV中的RGB Channel與Alpha Channel個別Render成兩個檔案,並且將Alpha Channel轉為黑與白的灰階影片。

image

上圖是MOV原始透明影片的樣子。

 

image

透過Adobe AfterEffect或者其他軟體,先輸出一個只有RGB的影片,如上圖。

 

image

接著輸出一個像這樣的灰階遮罩影片,如上圖。

(遮罩影片可以在Adobe AfterEffect當中,在Render時透過選擇 “Alpha Only" 作輸出。)

2. 然後將這兩支影片(RGB與Alpha)導入到Shader中

3. 以Alpha影片之任一色版(RGB都可以,因為在灰階影像中RGB皆相等)作為Mask來源,將其灰階數值套用至RGB影片之透明色版。

在實作完成後,我們就能透過Mask後得到一個完美的透明影片!

image

但完成後我們會馬上發現,由於實作上是使用兩個MediaElement實作而成,因此在讀取及播放的時候會衍生出另一個問題────兩支影片不同步。

所以我們可能會時不時地看到以下的結果:

image

(放大來看一下,能明顯地看出不同步所造成的黑影)

image

也就是Mask影片可能會因為與RGB影片不同步,而造成錯位的Mask結果。

所以這個時候我們該怎麼辦?

能做的解決辦法就是盡量讓兩支影片(RGB & Alpha)盡量同步。

那要透過什麼方式呢,多執行緒?定時對齊播放時間?

在想著如何盡量同步影片的同時,其實不難想像,就算再怎麼樣盡力讓兩支影片同步,這個結果終究會因為不同的主機速度或影片尺寸而有所誤差,即使是極小的時間誤差,當在Mask影片上有高速移動的物體時,誤差也會被放大。

 

Masking RGB Channel w/ Alpha Channel Video itself (MergedVideo)

因此為了徹底解決同步影片的問題,我選擇了另一個方式來同步───

直接將RGB Channel與Alpha Channel合併在同一支影片。

image

沒錯,就是將兩支影片上下合併同一支影片!

這樣子無論如何,兩支影片,絕對絕對都會是同步的。

所以在將兩支影片用影片編輯軟體上下合併在一起之後,重寫了一下Shader,讓下半部50%的Alpha影片當作上半部50%RGB影片的Mask,最後只要顯示上半部50%的影像,即可達到絕對同步的透明影片效果!

image

登登!終於達成完美的透明影片效果囉!

 

成果影片 – 爆炸特效

 

成果影片 – 火焰特效

 

範例原始檔下載

Download “20140115_AlphaPlayback_Demo” Demo.zip – Downloaded 4 times – 23 MB


Viewing all articles
Browse latest Browse all 8