HoloToolkit项目是一组开发包,是微软基于unity内置的底层API封装的一套工具集合,帮助我们快速使用Unity集成开发hololens应用。
本文主要通过源码研究其中Spatial Mapping的实现,学会如何使用实现自己的三维空间重建。

0x00 组件结构
Spatial Mapping目录下有很多内容,其中Prefabs目录里有我们可以直接使用的预置组件,本文关注的重点是Scripts目录的脚本。组件目录结构如下:

本文重点研究SpatialMappingObserver.cs和SpatialMappingSource.cs,这是当前组件的核心内容。

0x01 SpatialMappingObserver.cs
源码地址:https://github.com/Microsoft/HoloToolkit-Unity/blob/master/Assets/HoloToolkit/SpatialMapping/Scripts/SpatialMappingObserver.cs
我们先分析其属性,如下:
[代码]:
02 | public float TrianglesPerCubicMeter = 500f; |
05 | public Vector3 Extents = Vector3.one * 10.0f; |
08 | public float TimeBetweenUpdates = 3.5f; |
11 | private SurfaceObserver observer; |
14 | private Diction<a href= "http://www.52vr.com/armr/" style= "font-weight: bold;color: ;" target= "_blank" >AR</a>y< int , gameobject= "" > surfaces = new Dictionary< int , gameobject= "" >(); |
17 | private Queue<surfacedata> surfaceWorkQueue = new Queue<surfacedata>(); |
20 | private bool surfaceWorkOutstanding = false ; |
23 | private float updateTime; |
26 | public ObserverStates ObserverState { get ; private set ; }</surfacedata></surfacedata></ int ,></ int ,> |
再分析其程序逻辑及流程:
- Awake()方法最先被执行,这里对SurfaceObserver对象进行了初始化。
[代码]:
3 | observer = new SurfaceObserver(); |
4 | ObserverState = ObserverStates.Stopped; |
- 接下来是Start()方法,里面设定了扫描的空间范围。
[代码]:
3 | observer.SetVolumeAsAxisAlignedBox(Vector3.zero, Extents); |
- Update()方法中则会根据当前状态来调用API请求空间表面信息或者生成mesh对象
[代码]:
04 | if (ObserverState == ObserverStates.Running) |
07 | if (surfaceWorkOutstanding == false && surfaceWorkQueue.Count > 0) |
10 | SurfaceData surfaceData = surfaceWorkQueue.Dequeue(); |
13 | surfaceWorkOutstanding = observer.RequestMeshAsync(surfaceData, SurfaceObserver_OnDataReady); |
16 | else if (surfaceWorkOutstanding == false && (Time.time - updateTime) >= TimeBetweenUpdates) |
18 | observer.Update(SurfaceObserver_OnSurfaceChanged); |
19 | updateTime = Time.time; |
- SurfaceObserver_OnDataReady()事件方法用于处理使用SurfaceData请求到的mesh对象信息,用于后续的使用,比如处理其材质效果等。
[代码]:
01 | private void SurfaceObserver_OnDataReady(SurfaceData cookedData, bool outputWritten, float elapsedCookTimeSeconds) |
04 | if (surfaces.TryGetValue(cookedData.id.handle, out surface)) |
07 | MeshRenderer renderer = surface.GetComponent<meshrenderer>(); |
08 | renderer.sharedMaterial = SpatialMappingManager.Instance.SurfaceMaterial; |
10 | renderer.enabled = SpatialMappingManager.Instance.DrawVisualMeshes; |
12 | if (SpatialMappingManager.Instance.CastShadows == false ) |
14 | renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; |
18 | surfaceWorkOutstanding = false ; |
- SurfaceObserver_OnSurfaceChanged()事件方法用于处理SurfaceObserver获取到的空间表面数据,用于后续的请求mesh对象操作。
[代码]:
01 | private void SurfaceObserver_OnSurfaceChanged(SurfaceId id, SurfaceChange changeType, Bounds bounds, System.DateTime updateTime) |
04 | if (ObserverState != ObserverStates.Running) |
14 | case SurfaceChange.Added: |
15 | case SurfaceChange.Updated: |
17 | if (!surfaces.TryGetValue(id.handle, out surface)) |
20 | surface = AddSurfaceObject( null , string .Format( "Surface-{0}" , id.handle), transform); |
22 | surface.AddComponent<worldanchor>(); |
25 | surfaces.Add(id.handle, surface); |
29 | QueueSurfaceDataRequest(id, surface); |
32 | case SurfaceChange.Removed: |
34 | if (surfaces.TryGetValue(id.handle, out surface)) |
36 | surfaces.Remove(id.handle); |