Unity 中的自定義相機實現 —— 外部幀數據源
通過外部幀數據源(ExternalFrameSource),開發者可以爲 EasyAR Sense 擴展自定義的相機實現,從而支持特定的頭顯設備或其它輸入設備。以下內容介紹了外部幀數據源的類型結構及接口定義。
開始之前
外部幀數據源類型
---
config:
class:
hideEmptyMembersBox: true
---
classDiagram
class FrameSource {
<<abstract>>
}
class ExternalFrameSource {
<<abstract>>
}
class ExternalDeviceFrameSource {
<<abstract>>
}
class ExternalDeviceMotionFrameSource:::EasyAR {
<<abstract>>
}
class ExternalDeviceRotationFrameSource:::EasyAR {
<<abstract>>
}
class ExternalImageStreamFrameSource:::EasyAR {
<<abstract>>
}
ExternalFrameSource --|> FrameSource
ExternalDeviceFrameSource --|> ExternalFrameSource
ExternalDeviceMotionFrameSource --|> ExternalDeviceFrameSource
ExternalDeviceRotationFrameSource --|> ExternalDeviceFrameSource
ExternalImageStreamFrameSource --|> ExternalFrameSource
classDef EasyAR fill:#6e6ce6,stroke:#333,color:#fff
上圖展示了外部幀數據源的類型結構。
根據輸入數據的不同,外部幀數據源可以分爲兩大類:
- 圖像和設備運動數據輸入擴展
- 通過繼承 ExternalDeviceMotionFrameSource 實現:設備及設備 SDK 提供 6DoF 運動跟蹤功能。虛擬攝像機的 transform 及其它控制由設備 SDK 完成。
- 通過繼承 ExternalDeviceRotationFrameSource 實現:設備及設備 SDK 提供 3DoF 旋轉跟蹤功能。虛擬攝像機的 transform 及其它控制由設備 SDK 完成。
- 圖像輸入擴展
- 通過繼承 ExternalImageStreamFrameSource 實現:僅提供圖像輸入。虛擬攝像機的 transform 及其它控制由 EasyAR 完成。
接入這幾種外部幀數據源時,可以使用的 AR 功能有所不同:
- 圖像和設備運動數據輸入擴展 ExternalDeviceMotionFrameSource
- Mega
- 運動跟蹤(由設備自身提供)
- 稀疏空間地圖
- 稠密空間地圖
- 圖像跟蹤(支持運動融合)
- 圖像雲識別
- 物體跟蹤(支持運動融合)
- 圖像和設備運動數據輸入擴展 ExternalDeviceRotationFrameSource
- Mega
- 圖像跟蹤(不支持運動融合)
- 圖像雲識別
- 物體跟蹤(不支持運動融合)
- 圖像輸入擴展 ExternalImageStreamFrameSource
- 圖像跟蹤(不支持運動融合)
- 圖像雲識別
- 物體跟蹤(不支持運動融合)
外部幀數據源接口定義
創建外部幀數據源時,必須實現相關接口。下面介紹了這些接口的定義及使用方法。
設備定義
FrameSource.IsHMD:
定義是否爲頭顯
在且僅在頭顯設備上設爲 true。
如果設備是頭顯,診斷信息將顯示在攝像機前的 3D 板子而非屏幕上。部分 AR 功能在頭顯設備上運行會有些許不同。FrameSource.Display:
定義顯示系統
提供當前顯示的旋轉等信息。
可以使用 Display.DefaultSystemDisplay 或 Display.DefaultHMDDisplay 來獲取默認的顯示信息。 通常在頭顯上可以使用 Display.DefaultHMDDisplay。
可用性
- FrameSource.IsAvailable:
可用性(Availability)
用於判斷 frame source 是否可以使用。
如果一個 frame source 在當前運行設備或環境下不可用,該數值應爲 false。
如果該數值等於 Optional<bool>.Empty,FrameSource.CheckAvailability() 協程會被調用,應在協程結束前更新 FrameSource.IsAvailable。
可用性接口會在 session 組裝時使用,不可用的組件將不會被選擇且它的方法在 session 運行時不會被調用。 - FrameSource.CheckAvailability()(可選):
檢查 frame source 是否可用的協程
FrameSource.IsAvailable 等於 Optional<bool>.Empty 時會被調用。在該協程結束前,session 的組裝過程會被阻塞。
session 原點
ExternalDeviceFrameSource.OriginType:
原點類型- XROrigin:設備 SDK 使用 Unity.XR.CoreUtils.XROrigin 作爲原點。
- Custom:設備 SDK 使用自定義的原點。 需指定 ExternalDeviceFrameSource.Origin。
- None:設備 SDK 未定義原點。
這時原點將會自動從場景中選擇或創建,但不會移動。
session 將只支持 SessionOrigin 中心模式。應用開發者必須對於他們如何擺放虛擬物體十分小心,因爲所有 target 及 target 下的內容永遠都會在 Unity 座標系中移動,用戶的部分內容(比如物理系統)將無法正常工作。所有放在 Unity 世界座標系下的物體在任何配置下都永遠不可能顯示在正確的位置。
ExternalDeviceFrameSource.Origin:
原點物體
在且僅在 ExternalDeviceFrameSource.OriginType 爲 Custom 時定義自己的原點,其它時候不需要重新定義。
虛擬攝像機
- FrameSource.Camera:
虛擬攝像機
攝像機不受 session 控制,攝像機的 transform 和投影矩陣以及圖像背景渲染應由外部代碼控制。
僅在頭顯上該攝像機會被使用,用於將一些診斷文字展示在眼前。
在 ExternalDeviceFrameSource.OriginType 是 XROrigin 時不需要定義,EasyAR 會自動使用 Unity XR 框架中定義的相機。
物理相機
- FrameSource.DeviceCameras:
物理相機參數
提供相機幀數據的物理相機。如果相機幀數據由多個相機提供,列表中需要包含所有物理相機。
需要確保在 FrameSource.CameraFrameStarted 爲 true 時可以獲取到正確的物理相機參數。 - FrameSource.CameraFrameStarted:
相機幀是否開始輸入
在物理相機準備好並可以輸入數據到 EasyAR 之後返回 true,物理相機停止運行後返回 false。在 FrameSource.CameraFrameStarted 爲 false 時,EasyAR 不會工作。FrameSource.CameraFrameStarted 爲 true 時必須保證 FrameSource.DeviceCameras 數據可以訪問且不間斷地向 EasyAR 輸入相機幀數據。在 EasyAR 檢查到相機幀連續長時間無輸入後會彈出警告,輔助用戶判斷功能無響應時進行問題解耦。
物理相機參數需要與真實設備相機相同。
- FrameSourceCamera.CameraType:
物理相機類型
一般非前置相機的情況,比如頭顯上,選擇後置相機。 - FrameSourceCamera.CameraOrientation:
物理相機圖像在設備的自然方向上顯示時需要順時針旋轉的角度
範圍爲 [0, 360)。 - FrameSourceCamera.FrameSize:
圖像尺寸 - FrameSourceCamera.FrameRateRange:
幀率範圍
定義 x 爲幀率範圍下界 y 爲幀率範圍上界。 - DeviceFrameSourceCamera.AxisSystem:
頭/物理相機 pose 以及物理相機外參使用的座標軸系統
所有矩陣必須使用相同的座標軸系統。如果使用的數據定義不符合已知的系統,需要在傳給 EasyAR 之前進行座標軸變換。 - DeviceFrameSourceCamera.Extrinsics:
物理相機外參
一般是標定的矩陣。其座標軸應符合 DeviceFrameSourceCamera.AxisSystem 定義。如果外參的座標軸定義與實際 pose 的座標軸定義不同或它們不符合 DeviceFrameSourceCamera.AxisSystem 的定義,需要在設置這個數值之前進行座標軸變換。
session 啓動和停止
- FrameSource.OnSessionStart(ARSession):
處理 session 啓動事件
在 session 組裝時選擇了這個 frame source 時有效。
可以用於延遲初始化,在這個方法中進行 AR 獨有的初始化工作。 - FrameSource.OnSessionStop():
處理 session 停止事件
在 session 組裝時選擇了這個 frame source 時有效。
可以在這個方法中銷燬 FrameSource.OnSessionStart(ARSession) 以及 session 運行中創建的資源並恢復內部狀態。在 session 銷燬之前這個方法會被保證調用。如果 frame source 在 session 之前銷燬,它將不會被調用,且 session 將進入 Broken 狀態。
輸入幀
ExternalDeviceRotationFrameSource.HandleCameraFrameData(DeviceFrameSourceCamera, double, Image, CameraParameters, Quaternion):
輸入相機幀數據
可以在任何線程調用,只要設備 SDK 的 API 都是線程安全的即可。
這些數據需要與物理相機傳感器曝光時的數據一致。建議輸入 30 或 60 fps 的數據。最小可接受幀率爲 2,但部分算法響應時間會受影響。只要可以獲取,建議輸入彩色數據,這對 Mega 的效果是有幫助的。
爲實現最佳效率,可以設計整個數據鏈條讓原始 YUV 數據直接通過共享內存透傳,並直接使用數據指針傳入 EasyAR,並注意數據所有權。ExternalDeviceMotionFrameSource.HandleRenderFrameData(double, Pose, MotionTrackingStatus):
輸入渲染幀數據ExternalDeviceRotationFrameSource.HandleRenderFrameData(double, Quaternion):
輸入渲染幀數據
需要確保在設備數據準備好之後每個渲染幀調用,不能跳幀。這些數據需要與驅動同一幀內當前 Unity 虛擬攝像機的數據一致。
- ExternalFrameSource.TryAcquireBuffer(int):
嘗試從內存池中獲取內存塊
這個內存塊通常用於存儲相機幀的圖像數據並輸入 EasyAR。 - ExternalFrameSource.ReceivedFrameCount:
EasyAR 獲取到的相機幀計數
EasyAR 會用它來檢查設備相機幀輸入的健康情況。可以在調試中使用,如果這個數值停止增長,通常說明設備停止向 EasyAR 輸入數據。
Unity 消息
在腳本中使用以下消息時,需要注意確保基類實現被調用:
後續步驟
- 閱讀 外部輸入幀數據 瞭解相機幀數據和渲染幀數據
- 創建 圖像和設備運動數據輸入擴展
- 創建 圖像輸入擴展