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程式碼上新增,修改一部分:
-
Properties
-
{
-
//原始程式碼保留
-
_Num("Num",float) = 0.5 //方便除錯的引數設定
-
}
-
Tags { "Queue"="Transparent" "RenderType"="Transparent"}
-
//修改標籤,告訴引擎何時如何渲染這個物件
-
//標籤是標準的鍵值對,常用如下:
-
//Queue 佇列標籤,決定物件渲染次序
-
//著色器決定物件所歸屬的渲染佇列,任何透明物體可以通過這種方法在渲染不透明物體之後渲染。
-
//ShaderLab中有四種預定義的渲染佇列
-
//BackGround 後臺,這個渲染佇列在所有佇列前被渲染,用於渲染天空盒子之類的
-
//Geometry 幾何體,這個是預設佇列,用於大多數物件,不透明幾何體大多用這個佇列
-
//TransParent 透明,這個渲染佇列在幾何體佇列之後被渲染,採用由後到前的次序。任何採用Alpha的混合物件(不對深度緩衝產生寫操作的著色器)都在這裡渲染。
-
//Overlay 覆蓋 實現疊加效果,任何需要最後渲染的物件都應該放在此處
-
#pragma surface surf NoLighting alpha:auto
-
//開啟alpha:auto 自動混合Alpha 而且不要光照
-
//新增
-
//Alpha決定了貼圖的透明度
-
fixed4 LightingNoLighting(SurfaceOutput s, fixed3 lightDir, fixed atten)
-
{
-
fixed4 c;
-
c.rgb = s.Albedo;
-
c.a = s.Alpha;
-
return c;
-
}
-
//別忘了宣告一下_Num
-
float _Num;
-
//表面著色程式:
-
void surf (Input IN, inout SurfaceOutput o) //表面著色函式 每個頂點的顏色 都在 o 中反映
-
{
-
o.Emission = tex2D(_MainTex, IN.uv_MainTex).rgb;
-
//這裡給輸出的Alpha賦值
-
//由於我的視訊沒處理好,不是很對稱。。。
-
//對稱的話應該是0.5
-
//這裡和0.43比較的是UV貼圖的x軸的座標(0~1,0.5就表示橫座標的一半)
-
//右半邊視訊不顯示,所以賦值alpha=0
-
if (IN.uv_MainTex.x >= 0.43)
-
{
-
o.Alpha = 0;
-
}
-
else
-
{
-
//左半邊視訊的Alpha值和右半邊黑白視訊的RGB的值一樣
-
//因為我這邊處理的A黑白視訊不是很好,所以獲得了右半邊UV的RGB後得比較一下
-
//再給Alpha賦值
-
o.Alpha = tex2D(_MainTex, float2(IN.uv_MainTex.x + 0.43, IN.uv_MainTex.y)).rgb;
-
}
-
}
寫好了Shader,再把這個Shader給Plan上的材質。
最終效果如下:(我的視訊沒處理好,可能不夠對稱,效果不理想)
2.視訊+Shader實現
這次視訊只用到了純黑色背景的視訊,所以在Shader中我們要將黑色剔除。
新建一個UnlitShader 下面是Shader程式碼
-
Shader "Unlit/Transparent Chroma" { //扣除黑色
-
Properties {
-
_MainTex ("Base (RGB)", 2D) = "white" {}
-
_MaskCol ("Mask Color", Color) = (1.0, 0.0, 0.0, 1.0)
-
_Sensitivity ("Threshold Sensitivity", Range(0,1)) = 0.5 //敏感程度
-
_Smooth ("Smoothing", Range(0,1)) = 0.5
-
}
-
SubShader {
-
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
-
LOD 100
-
ZTest Always Cull Back ZWrite On Lighting Off Fog { Mode off }
-
CGPROGRAM
-
#pragma surface surf Lambert alpha:auto
-
struct Input
-
{
-
float2 uv_MainTex;
-
};
-
sampler2D _MainTex;
-
float4 _MaskCol;
-
float _Sensitivity;
-
float _Smooth;
-
void surf (Input IN, inout SurfaceOutput o) {
-
half4 c = tex2D (_MainTex, IN.uv_MainTex);
-
float maskY = 0.2989 * _MaskCol.r + 0.5866 * _MaskCol.g + 0.1145 * _MaskCol.b;
-
float maskCr = 0.7132 * (_MaskCol.r - maskY);
-
float maskCb = 0.5647 * (_MaskCol.b - maskY);
-
float Y = 0.2989 * c.r + 0.5866 * c.g + 0.1145 * c.b;
-
float Cr = 0.7132 * (c.r - Y);
-
float Cb = 0.5647 * (c.b - Y);
-
float blendValue = smoothstep(_Sensitivity, _Sensitivity + _Smooth, distance(float2(Cr, Cb), float2(maskCr, maskCb)));
-
o.Alpha = 1.0 * blendValue;
-
o.Emission = c.rgb * blendValue;
-
}
-
ENDCG
-
}
-
FallBack "Diffuse"
-
}
在這邊調整至合適得效果,得到結果如下
二、開啟相機實現視訊播放,大屏互動
這裡使用的是第一種方法,即兩個視訊,一個彩色,一個黑白。
首先新建一個RawImage,用來顯示相機拍攝到的畫面,再把Cavas的渲染模式設定成ScreensSpaceCamera,將RendererCamera設定成MainCamera,然後將之前的Plane調整到合適的位置。
我們用指令碼開啟攝像頭,在進行下一步操作。
-
using System.Collections;
-
using System.Collections.Generic;
-
using UnityEngine;
-
using UnityEngine.UI;
-
using UnityEngine.Video; //VideoPlayer的名稱空間
-
public class UsingCamera : MonoBehaviour
-
{
-
public WebCamTexture webTex; //相機捕捉到的圖片
-
public string deviceName; //相機裝置名
-
public Button startBT, pauseBT, StopBT;
-
public RawImage rowImg; //相機畫面展示
-
public VideoClip playClip; //要播放的視訊
-
public VideoPlayer videoPlayer; //Plane的VideoPlayer
-
public Material mat; //平面材質
-
// Use this for initialization
-
void Start()
-
{
-
StartCoroutine(CallCamera()); //呼叫相機
-
videoPlayer.clip = null;
-
}
-
// Update is called once per frame
-
void Update()
-
{
-
if (webTex != null)
-
{
-
rowImg.texture = webTex;
-
}
-
}
-
IEnumerator CallCamera() //開啟攝像頭的協程
-
{
-
yield return Application.RequestUserAuthorization(UserAuthorization.WebCam);//獲取使用者許可
-
if (Application.HasUserAuthorization(UserAuthorization.WebCam))
-
{
-
WebCamDevice[] devices = WebCamTexture.devices;
-
deviceName = devices[0].name;
-
//設定攝像機的區域和幀率
-
webTex = new WebCamTexture(deviceName, Screen.width, Screen.height, 20);
-
webTex.Play();//開始
-
}
-
}
-
}
由於視訊在開始時不載入,要觸發某個事件才播放,這裡先將VideoPlayer的PlayOnAwake去掉,然後新增觸發,這裡我們用三個Button來觸發視訊載入播放,載入並開始,暫停,結束並銷燬。
在場景中在建三個按鈕
新增按鍵指令碼:
-
新增按鍵指令碼
-
//按鍵呼叫
-
public void OnStartBTClick()
-
{
-
if (videoPlayer.clip == null)
-
{
-
videoPlayer.clip = playClip;
-
videoPlayer.Play();
-
}
-
}
-
public void OnPauseBTClick()
-
{
-
if(videoPlayer.clip != null)
-
videoPlayer.Pause();
-
}
-
public void OnStopBTClick()
-
{
-
if (videoPlayer.clip != null)
-
{
-
videoPlayer.Stop();
-
videoPlayer.clip = null;
-
}
-
}
最後將指令碼掛在一個物體上,並賦值,再將Button事件賦值。
但是這樣做會在載入或者不載入的時候有白色的圖片擋著:
因此就要在這邊動態修改這個_Num來調整透明度
將指令碼中Start和按鍵事件新增程式碼:
-
void Start()
-
{
-
//
-
mat.SetFloat("_Num", 0); //為了保證載入的時候沒有白色的遮擋
-
}
-
IEnumerator startPlayVideo() //如果開始設定太快還是會有白色閃爍,所以用了協程延時
-
{
-
yield return new WaitForSeconds(0.2f);
-
mat.SetFloat("_Num", 0.43f);
-
}
-
//按鍵呼叫
-
public void OnStartBTClick()
-
{
-
if (videoPlayer.clip == null)
-
{
-
videoPlayer.clip = playClip;
-
videoPlayer.Play();
-
StartCoroutine(startPlayVideo());
-
}
-
}
-
public void OnStopBTClick()
-
{
-
if (videoPlayer.clip != null)
-
{
-
mat.SetFloat("_Num", 0);
-
}
-
}
現在能達到預期的效果了