本文主要討論如何在Unity項目中集成空間映射功能。Unity內置了對空間映射功能的支持,通過以下兩種方式提供給開發者:
- HoloToolkit項目中你可以找到空間映射組件,這可以讓你便捷快速地開始使用空間映射特性。
- Unity還提供更多底層的空間映射API,以便開發者能夠完全控制空間映射特性,滿足定制復雜的應用需求
為了在應用使用空間映射特性,你必須在應用權限清單中啟用SpatialPerception能力。
Setting the SpatialPerception capability 設置SpatialPerception能力
為了使應用能夠使用空間映射數據,SpatialPerception能力必須被啟用。
使用以下步驟啟用此能力:
- 在Unity編輯器中,進入Player Settings選項(Edit > Project Settings > Player)
- 點擊Window Store選項卡
- 展開Publish Settings選項,並在Capabilities列表勾選SpatialPerception選項
注意:如果你已經把Unity項目導出為Visual Studio項目,你需要重新導出修改后的項目到新文件夾或者手動在VS中修改AppxManifest應用清單。
空間映射特性也要求項目MaxVersionTested版本最低為10.0.10586.0:
- 在VS項目解決方案中,雙擊Package.appxmanifest文件,並右鍵選中查看源碼方式打開
- 找到TargetDeviceFamily這一行,並將MaxVersionTested="10.0.10240.0" 修改為 MaxVersionTested="10.0.10586.0"
- 保存Package.appmanifest文件
Spatial mapping components 空間映射組件
HoloToolkit項目提供了幾個方案幫助你簡單快速集成空間映射特性。
對於默認的空間映射需求,我們推薦使用SpatialMappingComponent目錄下的 SpatialMappingCollider.cs 和 SpatialMapppingRenderer.cs腳本。如果你需要從網絡或者文件載入空間網格,可以使用SpatialMapping目錄下的腳本。
額外的信息可以在HoloToolkit項目Github主頁上找到。
How to use the API 如何使用底層API
命名空間: UnityEngine.VR.WSA
類型: SurfaceObserver, SurfaceChange, SurfaceData, SurfaceId
SurfaceObserver是主要使用到的API對象,下面是應用使用空間映射特性推薦的大致流程。
Set up the SurfaceObserver(s) 設定SurfaceObserver對象
你要為每一個需要空間映射數據的空間區域在應用中初始化一個SurfaceObserver對象。
SurfaceObserver surfaceObserver; void Start () { surfaceObserver = new SurfaceObserver(); }
通過調用SetVolumeAsSphere、SetVolumeAsAxisAlignedBox、 SetVolumeAsOrientedBox、 或 SetVolumeAsFrustum方法可以為每個SurfaceObserver對象指定它們需要獲取數據的空間范圍。以后你還可以通過再次調用它們來重新設定檢測的空間范圍。
void Start () { ... surfaceObserver.SetVolumeAsAxisAlignedBox(Vector3.zero, new Vector3(3, 3, 3)); }
當你調用SurfaceObserver.Update()方法時,需要每一個SurfaceObserver對象檢測區域中的空間表面(spatial surface)指定事件處理方法。
private void OnSurfaceChanged(SurfaceId surfaceId, SurfaceChange changeType, Bounds bounds, System.DateTime updateTime) { //處理空間表面變化 }
Handling Surface Changes 處理空間表面變化
關於空間表面變化,有幾個典型情形需要處理。Added狀態和Updated狀態可以使用相同的代碼處理,Removed狀態則使用另一種代碼來處理。
- 在Added和Updated情形下,我們從字典中添加或者獲取代碼當前網格的對象,使用必要的組件來創建一個SurfaceData結構體,然后調用RequestMeshDataAsync方法在場景中使用網格數據和位置來填充對象。
- 在Removed情形下,我們從字典中移除當前網格代表的對象並銷毀它。
System.Collections.Generic.Dictionary<SurfaceId, GameObject> spatialMeshObjects = new System.Collections.Generic.Dictionary<SurfaceId, GameObject>(); private void OnSurfaceChanged(SurfaceId surfaceId, SurfaceChange changeType, Bounds bounds, System.DateTime updateTime) { switch (changeType) { case SurfaceChange.Added: case SurfaceChange.Updated: if (!spatialMeshObjects.ContainsKey(surfaceId)) { spatialMeshObjects[surfaceId] = new GameObject("spatial-mapping-" + surfaceId); spatialMeshObjects[surfaceId].transform.parent = this.transform; spatialMeshObjects[surfaceId].AddComponent<MeshRenderer>(); } GameObject target = spatialMeshObjects[surfaceId]; SurfaceData sd = new SurfaceData( //系統返回的surface id, //當前對象的MeshFilter組件 target.GetComponent<MeshFilter>() ?? target.AddComponent<MeshFilter>(), //用於在空間中定位對象的空間錨 target.GetComponent<WorldAnchor>() ?? target.AddComponent<WorldAnchor>(), //當前網格對象的MeshCollider組件 target.GetComponent<MeshCollider>() ?? target.AddComponent<MeshCollider>(), //每立方米網格三角形的數量 1000, //bakeMeshes -如果是true,MeshCollider會被數據填充,反之MeshCollider為空 true ); SurfaceObserver.RequestMeshAsync(sd, OnDataReady); break; case SurfaceChange.Removed: var obj = spatialMeshObjects[surfaceId]; spatialMeshObjects.Remove(surfaceId); if (obj != null) { GameObject.Destroy(obj); } break; default: break; } }
Handing Data Ready 處理DataReady事件
OnDataReady事件方法會接收到一個SurfaceData對象,它包含了WorldAnchor、MeshFilter和MeshCollider對象數據,表示了當前關聯的空間表面最新狀態。通過訪問Mesh Filter對象的Mesh數據可以進行性能分析或者處理網格。使用最新的Mesh數據來渲染空間表面並將它用於物理碰撞或者射線擊中對象。確認SurfaceData內容不為空很重要。
Start processing on updates 處理更新操作
SurfaceObserver.Update()方法只能延時調用,可以每幀更新都調用。
void Start () { ... StartCoroutine(UpdateLoop()); } IEnumerator UpdateLoop() { var wait = new WaitForSeconds(2.5f); while(true) { surfaceObserver.Update(OnSurfaceChanged); yield return wait; } }
HoloToolKit
HoloToolkit項目是基於Unity API封裝的一系列很有用的全息開發代碼工具集合,能幫助開發者快速集成HoloLens特性。
Troubleshooting 問題診斷
-
確保你啟用了 SpatialPreception能力
- 當追蹤焦點丟失時,在接下來的OnSurfaceChanged事件處理中將會移除現有所有的網格。