1. 程式人生 > >unity透明視訊的實現的兩種方法

unity透明視訊的實現的兩種方法

AR中常見的應用方式,在攝像機前播放部分透明的視訊,讓視訊和相機中的場景有所互動等應用方式。這次主要介紹特殊Shader的編寫和視訊的簡易製作,在Unity中不借助ARSDK開啟攝像頭,播放視訊達到簡易的AR的效果。

這邊平面和視訊有兩種不同方法實現。下面分別介紹。

一、視訊的處理(兩種方式)

1.用原始視訊+黑白剪影+Shader視訊實現(一個視訊被分為左右兩部分)

大致介紹:將視訊分為兩部分,左邊部分為正常視訊,右邊部分為其黑白剪影。在Unity中通過Shader獲取右邊剪影響應位置的顏色資訊,如果右邊對應座標的顏色值為黑色,則左邊這部分響應Alpha值為0(透明),反之為1(不透明)。

1.1.視訊製作:

1.1.1首先準備視訊素材。素材儘量能夠”黑白分明”,不需要的部分儘量都為黑色,這樣做出來效果比較好。這個素材不自帶A通道,於是我們要想辦法讓他變得有A通道

 

 

因為我的視訊本身沒有Alpha通道,為了達到把黑色部分都透明掉,所以先給這個視訊加一個特效:顏色鍵

 

鍵顏色選擇黑色,然後調整引數 得到下面合適的效果

 

然後在該層下面新建一個白色固態層,軌道蒙版選擇為Alpha

 

這樣想要顯示的部分就出來了。

1.1.2 再新建一個合成,將之前那個合成拖到這個合成中,再將原視訊拖進來。修改縮放,位置讓他倆對稱。

 

然後匯出這個合成到mp4檔案。

如果你的素材自帶A通道,那麼同理步驟1.1.1,只是不用再加顏色鍵這個特效了

 這個固態層一定要在素材視訊的下面!

(由於我不是專門做AE的,只是摸著石頭過河,如果有大神有什麼更方便的的方法,歡迎指正~)

 

1.2.1 Shader的編寫。

剛才我們處理好了素材,接下來開始建立我們的Unity 專案。

建立一個Plane拖到相對相機合適的位置上,建立一個材質球,賦給Plane。

匯入剛才的視訊。

將MainCamera的投影方式設定成正交投影。

在Plane上新增VideoPlayer 並將視訊拖上去,看看視訊效果,再進行一次調整。我的視訊拖上去是上下顛倒了的,所以進行了一次旋轉。如果有音訊,將音訊拖到AudioSource上。

 

可以看到,播放視訊的時候,Material的貼圖變成了一個視訊,也就是說我們只要修改Shader就可以修改視訊的效果。

 

1.2.2 調整完畢後接下來開始寫Shader。

新建一個SurfaceShader ,開啟編寫。

在原始Shader程式碼上新增,修改一部分:

 
  1. Properties

  2. {

  3. //原始程式碼保留

  4. _Num("Num",float) = 0.5 //方便除錯的引數設定

  5.  
  6. }

  7. Tags { "Queue"="Transparent" "RenderType"="Transparent"}

  8. //修改標籤,告訴引擎何時如何渲染這個物件

  9. //標籤是標準的鍵值對,常用如下:

  10. //Queue 佇列標籤,決定物件渲染次序

  11. //著色器決定物件所歸屬的渲染佇列,任何透明物體可以通過這種方法在渲染不透明物體之後渲染。

  12. //ShaderLab中有四種預定義的渲染佇列

  13. //BackGround 後臺,這個渲染佇列在所有佇列前被渲染,用於渲染天空盒子之類的

  14. //Geometry 幾何體,這個是預設佇列,用於大多數物件,不透明幾何體大多用這個佇列

  15. //TransParent 透明,這個渲染佇列在幾何體佇列之後被渲染,採用由後到前的次序。任何採用Alpha的混合物件(不對深度緩衝產生寫操作的著色器)都在這裡渲染。

  16. //Overlay 覆蓋 實現疊加效果,任何需要最後渲染的物件都應該放在此處

  17.  
  18. #pragma surface surf NoLighting alpha:auto

  19. //開啟alpha:auto 自動混合Alpha 而且不要光照

  20.  
  21. //新增

  22. //Alpha決定了貼圖的透明度

  23. fixed4 LightingNoLighting(SurfaceOutput s, fixed3 lightDir, fixed atten)

  24. {

  25. fixed4 c;

  26. c.rgb = s.Albedo;

  27. c.a = s.Alpha;

  28. return c;

  29. }

  30.  
  31. //別忘了宣告一下_Num

  32. float _Num;

  33.  
  34. //表面著色程式:

  35. void surf (Input IN, inout SurfaceOutput o) //表面著色函式 每個頂點的顏色 都在 o 中反映

  36. {

  37. o.Emission = tex2D(_MainTex, IN.uv_MainTex).rgb;

  38.  
  39. //這裡給輸出的Alpha賦值

  40. //由於我的視訊沒處理好,不是很對稱。。。

  41. //對稱的話應該是0.5

  42. //這裡和0.43比較的是UV貼圖的x軸的座標(0~1,0.5就表示橫座標的一半)

  43. //右半邊視訊不顯示,所以賦值alpha=0

  44. if (IN.uv_MainTex.x >= 0.43)

  45. {

  46. o.Alpha = 0;

  47. }

  48. else

  49. {

  50. //左半邊視訊的Alpha值和右半邊黑白視訊的RGB的值一樣

  51. //因為我這邊處理的A黑白視訊不是很好,所以獲得了右半邊UV的RGB後得比較一下

  52. //再給Alpha賦值

  53. o.Alpha = tex2D(_MainTex, float2(IN.uv_MainTex.x + 0.43, IN.uv_MainTex.y)).rgb;

  54.  
  55. }

  56.  
  57. }

 

寫好了Shader,再把這個Shader給Plan上的材質。

最終效果如下:(我的視訊沒處理好,可能不夠對稱,效果不理想)

 

2.視訊+Shader實現

這次視訊只用到了純黑色背景的視訊,所以在Shader中我們要將黑色剔除。

新建一個UnlitShader 下面是Shader程式碼

 

 

 
  1. Shader "Unlit/Transparent Chroma" { //扣除黑色

  2. Properties {

  3. _MainTex ("Base (RGB)", 2D) = "white" {}

  4. _MaskCol ("Mask Color", Color) = (1.0, 0.0, 0.0, 1.0)

  5. _Sensitivity ("Threshold Sensitivity", Range(0,1)) = 0.5 //敏感程度

  6. _Smooth ("Smoothing", Range(0,1)) = 0.5

  7. }

  8. SubShader {

  9. Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}

  10. LOD 100

  11. ZTest Always Cull Back ZWrite On Lighting Off Fog { Mode off }

  12. CGPROGRAM

  13. #pragma surface surf Lambert alpha:auto

  14. struct Input

  15. {

  16. float2 uv_MainTex;

  17. };

  18.  
  19. sampler2D _MainTex;

  20. float4 _MaskCol;

  21. float _Sensitivity;

  22. float _Smooth;

  23.  
  24. void surf (Input IN, inout SurfaceOutput o) {

  25. half4 c = tex2D (_MainTex, IN.uv_MainTex);

  26.  
  27. float maskY = 0.2989 * _MaskCol.r + 0.5866 * _MaskCol.g + 0.1145 * _MaskCol.b;

  28. float maskCr = 0.7132 * (_MaskCol.r - maskY);

  29. float maskCb = 0.5647 * (_MaskCol.b - maskY);

  30.  
  31. float Y = 0.2989 * c.r + 0.5866 * c.g + 0.1145 * c.b;

  32. float Cr = 0.7132 * (c.r - Y);

  33. float Cb = 0.5647 * (c.b - Y);

  34.  
  35. float blendValue = smoothstep(_Sensitivity, _Sensitivity + _Smooth, distance(float2(Cr, Cb), float2(maskCr, maskCb)));

  36. o.Alpha = 1.0 * blendValue;

  37. o.Emission = c.rgb * blendValue;

  38. }

  39. ENDCG

  40. }

  41. FallBack "Diffuse"

  42. }


在這邊調整至合適得效果,得到結果如下 

 

二、開啟相機實現視訊播放,大屏互動

這裡使用的是第一種方法,即兩個視訊,一個彩色,一個黑白。

首先新建一個RawImage,用來顯示相機拍攝到的畫面,再把Cavas的渲染模式設定成ScreensSpaceCamera,將RendererCamera設定成MainCamera,然後將之前的Plane調整到合適的位置。

 

我們用指令碼開啟攝像頭,在進行下一步操作。

 
  1. using System.Collections;

  2. using System.Collections.Generic;

  3. using UnityEngine;

  4. using UnityEngine.UI;

  5. using UnityEngine.Video; //VideoPlayer的名稱空間

  6.  
  7. public class UsingCamera : MonoBehaviour

  8. {

  9.  
  10. public WebCamTexture webTex; //相機捕捉到的圖片

  11. public string deviceName; //相機裝置名

  12. public Button startBT, pauseBT, StopBT;

  13. public RawImage rowImg; //相機畫面展示

  14. public VideoClip playClip; //要播放的視訊

  15. public VideoPlayer videoPlayer; //Plane的VideoPlayer

  16. public Material mat; //平面材質

  17. // Use this for initialization

  18. void Start()

  19. {

  20. StartCoroutine(CallCamera()); //呼叫相機

  21. videoPlayer.clip = null;

  22. }

  23.  
  24. // Update is called once per frame

  25. void Update()

  26. {

  27. if (webTex != null)

  28. {

  29. rowImg.texture = webTex;

  30. }

  31. }

  32.  
  33. IEnumerator CallCamera() //開啟攝像頭的協程

  34. {

  35. yield return Application.RequestUserAuthorization(UserAuthorization.WebCam);//獲取使用者許可

  36. if (Application.HasUserAuthorization(UserAuthorization.WebCam))

  37. {

  38. WebCamDevice[] devices = WebCamTexture.devices;

  39. deviceName = devices[0].name;

  40. //設定攝像機的區域和幀率

  41. webTex = new WebCamTexture(deviceName, Screen.width, Screen.height, 20);

  42. webTex.Play();//開始

  43. }

  44.  
  45. }

  46. }

 

由於視訊在開始時不載入,要觸發某個事件才播放,這裡先將VideoPlayer的PlayOnAwake去掉,然後新增觸發,這裡我們用三個Button來觸發視訊載入播放,載入並開始,暫停,結束並銷燬。

在場景中在建三個按鈕

 

新增按鍵指令碼:

 

 
  1. 新增按鍵指令碼

  2. //按鍵呼叫

  3. public void OnStartBTClick()

  4. {

  5. if (videoPlayer.clip == null)

  6. {

  7. videoPlayer.clip = playClip;

  8. videoPlayer.Play();

  9. }

  10. }

  11.  
  12. public void OnPauseBTClick()

  13. {

  14. if(videoPlayer.clip != null)

  15. videoPlayer.Pause();

  16.  
  17. }

  18.  
  19. public void OnStopBTClick()

  20. {

  21. if (videoPlayer.clip != null)

  22. {

  23. videoPlayer.Stop();

  24. videoPlayer.clip = null;

  25. }

  26. }

 

最後將指令碼掛在一個物體上,並賦值,再將Button事件賦值。

 

但是這樣做會在載入或者不載入的時候有白色的圖片擋著:

 

 

因此就要在這邊動態修改這個_Num來調整透明度

 

將指令碼中Start和按鍵事件新增程式碼:

 
  1. void Start()

  2. {

  3. //

  4. mat.SetFloat("_Num", 0); //為了保證載入的時候沒有白色的遮擋

  5. }

  6.  
  7.  
  8. IEnumerator startPlayVideo() //如果開始設定太快還是會有白色閃爍,所以用了協程延時  

 
  1. {

  2. yield return new WaitForSeconds(0.2f);

  3. mat.SetFloat("_Num", 0.43f);

  4. }

  5.  
  6. //按鍵呼叫

  7. public void OnStartBTClick()

  8. {

  9. if (videoPlayer.clip == null)

  10. {

  11. videoPlayer.clip = playClip;

  12. videoPlayer.Play();

  13. StartCoroutine(startPlayVideo());

  14. }

  15. }

  16.  
  17. public void OnStopBTClick()

  18. {

  19. if (videoPlayer.clip != null)

  20. {

  21. mat.SetFloat("_Num", 0);

  22.  
  23. }

  24. }

現在能達到預期的效果了