HoloLens開發學習記錄--- 9.Spatial Mapping 空間映射 (將cube放到HoloLens掃描後的真實物體上)

       Hololens 作爲一款混合現實設備,其與傳統 VR/AR 設備最大的區別是,能夠和現實世界進行交互。

       以一個立方體爲例,當我們沒有使用 Spatial Mapping 時,我們只能在空間中移動它,而不能把它放置在現實世界的物體上,例如放置在一個椅子上。當我們使用了 Spatial Mapping 後,Hololens 會先掃描出所在房間的三維信息,掃描完畢後你就可以將物體放置在掃描後的空間物體上。

  一 , 用     unity2018.4.9  vs2017    創建一個新的 Unity 項目 VoiceDemo,初始化項目:

         
1.導入 MRTK 包                      (版本 HoloToolkit-Unity-2017.4.2.0)

2.應用項目設置爲 MR 項目       (一鍵設置成爲可以部署的環境)

3.使用 HoloLensCamera 替代默認相機

4.添加 CursorWithFeedback       (識別並反饋手勢的光標控件)

5.添加 InputManager                   (作爲輸入源管理器,管理 gaze,gesture,speech等)

6.設置 InputManager 的 SimpleSinglePointerSelector 腳本的 Cursor 屬性爲添加的 CursorWithFeedback      (添加手勢源到inputmanger)

7.創建一個空 GameObject,名爲 Manager,爲其添加子 gameObject: InputManager

8.添加一個 Cube

最終 Hierarchy 結構如下:

 

二、實現空間映射Spatial Mapping

1)添加 MRTK 工具包下的 SpatialMapping 預製體到 Manager 對象下。  //用於掃描當前的空間環境

        修改 Spatial Mapping Manager 的 Surface Material 屬性值爲 MRTK 包中的 SpatialUnderstandingSurface(空間理解時的表面網格材質),其他參數使用默認值即可,該屬性爲空間掃描時所使用的材質。

(2)在 Manager 下新建一個 GameObject(Create Empty ),名爲 SpatialProcessing

(3)爲 SpatialProcessing 添加以下兩個 MRTK 包中的腳本:

  • SurfaceMeshesToPlanes.cs
  • RemoveSurfaceVertices.cs

(4)新建腳本 SpatialProcessing.cs,並將其添加到 SpatialProcessing 上。

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System.Collections.Generic;
using UnityEngine;

namespace HoloToolkit.Unity.SpatialMapping.Tests
{
    public class SpatialProcessing : Singleton<SpatialProcessing>
    {
        [Tooltip("How much time (in seconds) that the SurfaceObserver will run after being started; used when 'Limit Scanning By Time' is checked.")]
        public float scanTime = 30.0f;

        [Tooltip("Material to use when rendering Spatial Mapping meshes while the observer is running.")]
        public Material defaultMaterial;

        [Tooltip("Optional Material to use when rendering Spatial Mapping meshes after the observer has been stopped.")]
        public Material secondaryMaterial;

        [Tooltip("結束處理所需要的最小floor數量")]
        public uint minimumFloors = 1;

        /// <summary>
        /// Indicates if processing of the surface meshes is complete.
        /// </summary>
        private bool meshesProcessed = false;

        /// <summary>
        /// GameObject initialization.
        /// </summary>
        private void Start()
        {
            // Update surfaceObserver and storedMeshes to use the same material during scanning.
            SpatialMappingManager.Instance.SetSurfaceMaterial(defaultMaterial);

            // Register for the MakePlanesComplete event.
            SurfaceMeshesToPlanes.Instance.MakePlanesComplete += SurfaceMeshesToPlanes_MakePlanesComplete;
        }

        /// <summary>
        /// Called once per frame.
        /// </summary>
        private void Update()
        {
            // Check to see if the spatial mapping data has been processed yet.
            if (!meshesProcessed)
            {
                // Check to see if enough scanning time has passed
                // since starting the observer.
                if ((Time.unscaledTime - SpatialMappingManager.Instance.StartTime) < scanTime)
                {
                    // If we have a limited scanning time, then we should wait until
                    // enough time has passed before processing the mesh.
                }
                else
                {
                    // The user should be done scanning their environment,
                    // so start processing the spatial mapping data...

                    if (SpatialMappingManager.Instance.IsObserverRunning())
                    {
                        // Stop the observer.
                        SpatialMappingManager.Instance.StopObserver();
                    }

                    // Call CreatePlanes() to generate planes.
                    CreatePlanes();

                    // Set meshesProcessed to true.
                    meshesProcessed = true;
                }
            }
        }

        /// <summary>
        /// Handler for the SurfaceMeshesToPlanes MakePlanesComplete event.
        /// </summary>
        /// <param name="source">Source of the event.</param>
        /// <param name="args">Args for the event.</param>
        private void SurfaceMeshesToPlanes_MakePlanesComplete(object source, System.EventArgs args)
        {
            // Collection of floor planes that we can use to set horizontal items on.
            List<GameObject> floors = new List<GameObject>();
            floors = SurfaceMeshesToPlanes.Instance.GetActivePlanes(PlaneTypes.Floor);

            // Check to see if we have enough floors (minimumFloors) to start processing.
            if (floors.Count >= minimumFloors)
            {
                // Reduce our triangle count by removing any triangles
                // from SpatialMapping meshes that intersect with active planes.
                RemoveVertices(SurfaceMeshesToPlanes.Instance.ActivePlanes);

                // After scanning is over, switch to the secondary (occlusion) material.
                SpatialMappingManager.Instance.SetSurfaceMaterial(secondaryMaterial);
            }
            else
            {
                // Re-enter scanning mode so the user can find more surfaces before processing.
                SpatialMappingManager.Instance.StartObserver();

                // Re-process spatial data after scanning completes.
                meshesProcessed = false;
            }
        }

        /// <summary>
        /// Creates planes from the spatial mapping surfaces.
        /// </summary>
        private void CreatePlanes()
        {
            // Generate planes based on the spatial map.
            SurfaceMeshesToPlanes surfaceToPlanes = SurfaceMeshesToPlanes.Instance;
            if (surfaceToPlanes != null && surfaceToPlanes.enabled)
            {
                surfaceToPlanes.MakePlanes();
            }
        }

        /// <summary>
        /// Removes triangles from the spatial mapping surfaces.
        /// </summary>
        /// <param name="boundingObjects"></param>
        private void RemoveVertices(IEnumerable<GameObject> boundingObjects)
        {
            RemoveSurfaceVertices removeVerts = RemoveSurfaceVertices.Instance;
            if (removeVerts != null && removeVerts.enabled)
            {
                removeVerts.RemoveSurfaceVerticesWithinBounds(boundingObjects);
            }
        }

        /// <summary>
        /// Called when the GameObject is unloaded.
        /// </summary>
        protected override void OnDestroy()
        {
            if (SurfaceMeshesToPlanes.Instance != null)
            {
                SurfaceMeshesToPlanes.Instance.MakePlanesComplete -= SurfaceMeshesToPlanes_MakePlanesComplete;
            }

            base.OnDestroy();
        }
    }
}

(5)設置 SpatialProcessing 預設體屬性如下:

Surface Meshes To Planes 腳本能夠將掃描的網格轉換爲實體。
          Draw Planes 爲需要轉換的類型。
          Destory Planes 爲需要丟棄的類型。
          這裏這兩個參數都使用了默認值,即保留了 Wall、Floor、Ceiling、Table 類型的網格數據。
Remove Surface Vertices 腳本能夠把與實體重合的網格刪除。
          SpatialProcessing 腳本用於處理網格數據。
          Scan Time : 掃描過多少秒開始轉換
          Default Material: 掃描時使用的材質,這裏使用 MRTK 包中的 WireframeBlue。
          secondaryMaterial: 停止掃描時使用的材質,這裏使用 MRTK 包中的 Occlusion,注意路徑是                                    HoloToolKit/SpatialMapping/Materials/Occlusion.mat。
          minimumFloors: 結束處理所需要的最小 floor 數量。


(6)爲 Cube 添加 MRTK 包下的 TapToPlace.cs 腳本。

(7)使用真機運行程序,不要忘記添加 SpatialPerception 權限:

實驗效果:

         程序啓動後,會先掃描空間信息。當掃描結束後,我們就可以把 Cube 放在實際的物體上,比如牆壁上。

 

三、Spatial UnderStanding 空間理解

 

       不知道你在運行上面的程序時,有沒有嘗試過,在掃描結束後你走動到之前沒有掃描到的地方,這時候就無法將Cube放置在實際的物體上了。

       這也很好理解,程序在啓動的一段時間內掃描空間數據,掃描結束後將其轉換爲(房屋)模型,你實際上放到的是在(房屋)模型上(不信你先掃描一個椅子,掃描結束後將椅子移走,Cube 只能放在椅子原來的位置上)。而我們之前沒有掃描到的地方,自然沒有(房屋)模型,因此無法放置。
       解決辦法:Hololens 爲我們提供了 Spatial UnderStanding 的功能,能夠讓 Hololens 實時掃描空間數據,實時更新(房屋)模型。當然這樣會佔用較大的 CPU 資源。

         MRTK 工具包爲我們提供了 SpatialUnderstanding,直接將其拖入 Manager 下即可。

重新運行程序,我們發現是在實時掃描的,掃描到的部分被藍色網格所覆蓋。

 

查看下開啓 SpatialUnderstanding 的 CPU 使用情況:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章