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 使用情况:

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