版權聲明:Davidwang原創文章,嚴禁用於任何商業途徑,授權後方可轉載。
在AR中播放視頻也是一種常見的需求,如在一個展廳中放置的虛擬電視上播放宣傳視頻,或者在遊戲中爲營造氛圍而設置的虛擬電視視頻,本節我們將學習如何在AR場景中播放視頻。
(一)VideoPlayer組件
VideoPlayer是Unity一個跨平臺播放視頻的組件,這個組件在播放視頻時會調用運行時系統本地的視頻解碼器,即其本身並不負責視頻的解碼,因此開發人員需要確保在特定平臺上視頻編碼格式能被支持。VideoPlayer不僅能播放本地視頻,也能夠通過http協議播放遠端服務器視頻,而且支持將視頻播放到攝像頭平面(Camera Plane,包括近平面和遠平面)、作爲渲染紋理(Render Texture)、作爲材質的紋理、其他組件的Texture屬性,因此,可以方便的將視頻播放到攝像頭平面、3D物體上、UI界面等,功能非常之強大。VideoPlayer組件界面如下圖所示:
VideoPlayer組件主要屬性意義如下表所示:
屬性 | 描述 |
---|---|
Source | 視頻源,其下拉菜單包括兩個選項:Video Clip與URL。選擇Video Clip時,可以將項目內的視頻直接賦給Video Clip屬性。當選擇URL時,可以使用視頻的路徑定位視頻(如使用http://或者file://),我們既可以在編譯前爲其設置固定的地址信息,也可以在運行時通過腳本代碼實時的修改這個路徑達到動態控制視頻加載的目的。 |
Play On Awake | 是否在場景加載後就播放視頻,如果不勾選該項,應當在運行時通過腳本來播放視頻。 |
Wait For First Frame | 該選項主要用來同步視頻播放與場景進度,特別是在遊戲場景中,如果勾選,Unity則會在遊戲開始前等待源視頻加載顯示,如果取消勾選,則可能會丟棄前幾幀以使視頻以使視頻播放與遊戲的其餘部分保持同步。在AR中建議不勾選,不勾選時視頻會跳幀。 |
Loop | 循環播放。 |
Skip On Drop | 是否允許跳幀以保持與場景同步,建議勾選。 |
Playback Speed | 視頻播放速度倍率,如設置爲2,視頻則以2倍速播放,範圍爲[0,10]。 |
Render Mode | 定義視頻的渲染方式。其下拉菜單中包括四個選項:Camera Far Plane、Camera Near Plane、Render Texture、Material Override、API Only。各渲染方式功能如下:1、Camera Far Plane:將視頻渲染到攝像機的遠平面,其Alpha值可設置視頻的透明度。2、Camera Near Plane:將視頻渲染到攝像機的近平面,其Alpha值可設置視頻的透明度。3、Render Texture:將視頻渲染爲Render Texture,通過設置Target Texture定義Render Texture組件渲染到其圖像的Render Texture。4、Material Override:將視頻渲染到物體材質上。5、API Only:不能使用腳本代碼將視頻渲染到VideoPlayer.texture屬性中。 |
Audio Output Mode | 定義音軌輸出,其下拉菜單包括四個選項:None、Audio Source、Direct、API only。選擇None時不播放音軌;選擇Audio Source時可以自定義音頻輸出;選擇Direct時,會直接將視頻中的音軌輸出;選擇API only時,只能使用腳本代碼控制音軌的設置。 |
Mute | 是否禁音。 |
Volume | 音頻音頻。 |
除此這外,VideoPlayer組件還有很多供腳本調用的屬性、方法及事件,常用的主要包括:
名稱 | 類型 | 描述 |
---|---|---|
EnableAudioTrack | 方法 | 啓用或禁用音軌,需要在視頻播放前設置。 |
Play | 方法 | 播放視頻。 |
Pause | 方法 | 暫停視頻播放。 |
Stop | 方法 | 方法 |
isPlaying | 屬性 | 視頻是否正在播放。 |
isLooping | 屬性 | 視頻是否是循環播放 。 |
isPrepared | 屬性 | 視頻是否已準備好(如加載是否完成可供播放)。 |
errorReceived | 事件 | 發生錯誤時回調。 |
frameDropped | 事件 | 丟幀時回調。 |
frameReady | 事件 | 新幀準備好時回調,這個回調會非常頻繁,使用需要謹慎。 |
loopPointReached | 事件 | 視頻播放結束時回調。 |
prepareCompleted | 事件 | 視頻資源準備好時回調。 |
started | 事件 | 視頻開始播放後回調。 |
VideoPlayer還有很多其他方法、屬性、事件,利用這些方法屬性可以開發出功能強大的視頻播放器。如前所述,VideoPlayer會調用運行時本地系統中的原生解碼器,因此爲確保在特定平臺視頻播放正常,需要提前瞭解運行系統的解碼能力,通常,不同的操作系統對視頻的原生解碼並不相同,下表爲當前各大系統對視頻格式的原生支持列表。
操作系統 | 支持的原生解碼格式 |
---|---|
Windows | .asf,.avi,.dv,.mv4,.mov,.mp4,.mpg,.mpeg,.ogv,.vp8,.webm,.wmv |
OSX | .dv,.m4v,.mov,.mp4,.mpg,.mpeg,.ogv,.vp8,.webm,.wmv |
Linux | .ogv,.vp8,.webm |
Android | .3gp,.mp4 ,.webm, .ts, .mkv |
iPhone | .m4v, .mp4, .mov |
特別需要提醒的是:即使視頻後輟格式相同也不能確保一定能解碼,因爲同一種格式會有若干種編碼方式,具體使用過程中需要查詢特定平臺的解碼格式。
(二)實現視頻播放
因爲AR中場景是實景的特殊性,以虛擬物體作爲視頻播放承載物是最好的選擇。使用UI及攝像機平面播放模式不太適合AR應用,視頻渲染模型使用Material Override非常有利用在模型特定區域播放視頻。因此首先我們需要製作一個預製體,在模型的特定區域採用Material Override模式播放視頻。
新建一個工程,並在Hierarchy窗口中新建一個空對象,命名爲TVs,將電視機模型文件拖放到該空對象下作爲其子對象並調整好大小尺寸,然後新建一個Quad,用這個Quad爲作視頻播放的載體,調整該Quad到電視機屏幕位置,處理好尺寸與位置關係。因爲我們採用Material Override模式,所以我們需要新建一個材質,命名爲videos,該材質直接選用Standard着色器,但不賦任何紋理及參數(因爲後面我們將使用視頻幀作爲紋理渲染到材質上),並將該材質賦給Quad對象。最後一步,爲Quad對象掛載VideoPlayer組件,並設置好視頻源等屬性,如下圖所示。
將TVs製作成Prefab,同時刪除Hierarchy場景中的TVs對象。爲將虛擬電視機放置到真實環境中,我們採取與以晚一樣的射線檢測放置方式,新建一個C#腳本,命名爲AppController,編寫如下代碼:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
using UnityEngine.UI;
[RequireComponent(typeof(ARRaycastManager))]
public class AppControler : MonoBehaviour
{
public GameObject spawnPrefab;
public int MaxTVnumber = 1;
public Button BtnPlay;
public Button BtnPause;
public Button BtnStop;
static List<ARRaycastHit> Hits;
private ARRaycastManager mRaycastManager;
private GameObject spawnedObject = null;
private ARPlaneManager mARPlaneManager;
private int mCurrentTVnumber = 0;
private UnityEngine.Video.VideoPlayer mVideoPlayer ;
private void Start()
{
Hits = new List<ARRaycastHit>();
mRaycastManager = GetComponent<ARRaycastManager>();
mARPlaneManager = GetComponent<ARPlaneManager>();
BtnPlay.transform.GetComponent<Button>().onClick.AddListener(()=> mVideoPlayer.Play());
BtnPause.transform.GetComponent<Button>().onClick.AddListener(() => mVideoPlayer.Pause());
BtnStop.transform.GetComponent<Button>().onClick.AddListener(() => mVideoPlayer.Stop());
}
void Update()
{
if (Input.touchCount == 0)
return;
var touch = Input.GetTouch(0);
if (mRaycastManager.Raycast(touch.position, Hits, TrackableType.PlaneWithinPolygon | TrackableType.PlaneWithinBounds) )
{
var hitPose = Hits[0].pose;
if (spawnedObject == null && mCurrentTVnumber < MaxTVnumber)
{
spawnedObject = Instantiate(spawnPrefab, hitPose.position, hitPose.rotation);
mVideoPlayer = spawnedObject.gameObject.transform.Find("Quad").gameObject.GetComponent<UnityEngine.Video.VideoPlayer>();
TrunOffPlaneDetect();
mCurrentTVnumber++;
}
else
{
spawnedObject.transform.position = hitPose.position;
}
}
}
void TrunOffPlaneDetect()
{
mARPlaneManager.enabled = false;
foreach (var plane in mARPlaneManager.trackables)
plane.gameObject.SetActive(false);
}
}
在上述代碼中,我們首先定義了三個按鈕(分別用於控制視頻播放、暫停、停止)及最大可放置虛擬電視機的數量(視頻解碼也是一項計算密集型任務,過多的虛擬電視播放視頻會嚴重應用程序性能),然後使用射線檢測的方式放置虛擬電視模型,並在放置模型後停止平面檢測和已檢測到的平面的顯示。
將AppController腳本掛載到Hierarchy場景中的AR Session Origin對象上,然後需要在場景中新建三個UI按鈕,並設置好腳本的相應屬性(將之前製作的TVs預製體設置到Spawn Prefab屬性,將三個按鈕設置到對應的按鈕屬性),如下圖所示。
編譯運行,在檢測到平面後點擊加載虛擬電視機,這時電視機上會播放設定的視頻,可以通過按鈕控制視頻的播放、暫停與停止,效果如下圖所示。
在播放視頻時,如果不勾選禁音,音頻是可以同步播放的,但播放的音頻並不具備空間定位能力,即聲音不會呈現空間特性,在AR中實現3D音頻功能需要與前文所述的空間音頻組件配合使用。