HoloLens開發手記 - Unity之Spatial mapping 空間映射


 

 

本文主要討論如何在Unity項目中集成空間映射功能。Unity內置了對空間映射功能的支持,通過以下兩種方式提供給開發者:

  1. HoloToolkit項目中你可以找到空間映射組件,這可以讓你便捷快速地開始使用空間映射特性。
  2. Unity還提供更多底層的空間映射API,以便開發者能夠完全控制空間映射特性,滿足定制復雜的應用需求

 

為了在應用使用空間映射特性,你必須在應用權限清單中啟用SpatialPerception能力。

 

 

Setting the SpatialPerception capability 設置SpatialPerception能力


 

為了使應用能夠使用空間映射數據,SpatialPerception能力必須被啟用。

使用以下步驟啟用此能力:

  1. 在Unity編輯器中,進入Player Settings選項(Edit > Project Settings > Player)
  2. 點擊Window Store選項卡
  3. 展開Publish Settings選項,並在Capabilities列表勾選SpatialPerception選項

 

注意:如果你已經把Unity項目導出為Visual Studio項目,你需要重新導出修改后的項目到新文件夾或者手動在VS中修改AppxManifest應用清單

空間映射特性也要求項目MaxVersionTested版本最低為10.0.10586.0

  1. 在VS項目解決方案中,雙擊Package.appxmanifest文件,並右鍵選中查看源碼方式打開
  2. 找到TargetDeviceFamily這一行,並將MaxVersionTested="10.0.10240.0" 修改為 MaxVersionTested="10.0.10586.0"
  3. 保存Package.appmanifest文件

 

Spatial mapping components 空間映射組件


 

HoloToolkit項目提供了幾個方案幫助你簡單快速集成空間映射特性。

對於默認的空間映射需求,我們推薦使用SpatialMappingComponent目錄下的 SpatialMappingCollider.csSpatialMapppingRenderer.cs腳本。如果你需要從網絡或者文件載入空間網格,可以使用SpatialMapping目錄下的腳本。

額外的信息可以在HoloToolkit項目Github主頁上找到。

 

How to use the API 如何使用底層API


 

命名空間UnityEngine.VR.WSA

類型: SurfaceObserverSurfaceChangeSurfaceDataSurfaceId

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事件處理中將會移除現有所有的網格。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM