2017 年 1 月

第 32 卷,第 1 期

此文章由机器翻译。

HoloLens - HoloLens 简介,第 2 部分: 空间映射

通过Adam Tuliper |2017 年 1 月

在我的上一篇文章中我谈 HoloLens 输入的三个支柱 — 对此、 手势和语音 (msdn.com/magazine/mt788624)。这些构造允许您以物理方式 HoloLens 和,接下来您周围世界交互。不被局限于仅使用它们,但是,因为您可以访问有关通过一种称为空间映射功能周围的环境的信息,我要在这篇文章中探讨。

如果我需要选择在 HoloLens 单个比较喜欢的功能,它将空间映射。空间映射,可以显式或隐式了解您,周围的空间。我可以显式选择处理中,执行的信息或通过允许自然物理交互,如删除到物理表,将执行的上一个虚拟球我可以继续在隐式。最近,有一些真的非常好的更新 HoloToolkit 从 Asobo Studio,很明显要扫描您的环境,如椅子、 墙壁和的详细信息中的功能。

三维模型是什么?

它可能有助于您了解哪些的 3D 模型是然后再查看您所在地区的空间映射所表示的内容。三维模型采用多种文件格式,如.ma 或.blender,但通常将在两种专有 Autodesk 格式的调用中找到它们。FBX (Filmbox) 或。OBJ 文件。.FBX 文件可以包含不仅 3D 模型的信息,而且还动画数据,但这并不是适用于本次讨论中。

三维模型是一个相当简单对象,通常通过面顶点网格跟踪这意味着跟踪平面和顶点。对于几乎所有现代硬件,三角形用于面由于三角形是最简单的多边形。三维模型内会在模型中发现的所有顶点列表 (由组成 x、 y、 z 值的空间中);构成每个三角形; 顶点索引的列表应该与您的模型; 交互都只是描述性向量 (箭头) 即将关闭用于反射计算,因此您知道如何细的每个顶点的法向并且,最后,UV 协调 — 实质上是 X,Y 坐标,告诉您如何采用 2D 图像,称为纹理,而环绕如包装纸以使其看起来像设计您的模型。图 1显示虚拟 Adam,公司 xxArray 创建对我来说是因为,嗯,我想要将自己放到场景中具有僵停的模型。这是只是一个三维模型,但请注意条腿,其中进行的顶点和三角形和短裤纹理,简单地说,环绕腿看起来像大受青睐的 3D 模型。这就是 3D 模型背后的几乎所有的魔力。

UV 映射到 3D 物体的 2D 纹理的
图 1 UV 映射到 3D 物体的 2D 纹理

空间映射看起来什么?

空间的映射是环境的在某些方面更容易,因为您不处理您的纹理。所有这一切通常关心,让创建从您的环境中可发现的相当准确网格。环境被扫描,因此可以与其进行交互。图 2略有更像您将实际获取的内容显示了一个方案,但专门设计。在左侧的模型显示顶点、 三角形和 normals。您不能正常直接查看,当然,但该对象是灰色的方式查看其结果。

所需来呈现和物理引擎
图 2 时有哪些要求来呈现和物理引擎

您已了解到目前为止这两种三维模型方案中是仅用于呈现,绝对没有任何关系 (尚) 与物理引擎。在右侧的绿色框轮廓图 2是形状的碰撞体我移动关闭多维数据集以显示一个点; 这是对物理引擎系统定义的区域的组件。如果希望完全与世界上 HoloLens,游戏或任何 3D 的经验,真正交互,你需要物理引擎系统碰撞体来使用。

当您打开 HoloLens,并在全息外壳程序时,它始终将映射您的环境。HoloLens 这样做是为了了解在何处放置您的 windows。如果我使用 HoloLens 房屋中四处走动,它将始终更新其有关我的环境的信息。这有两个用途︰ 首先,当我走进一的房间我就一直在以前,HoloLens 将显示我我最近打开的窗口。其次,也始终变化的环境,它需要检测这些更改。下面的常见情形的思考︰ 某人前面我将指导、 我的孩子都在屋子里运行方面的问题,我们宠物玩具熊的指导,并创建较大的封闭区域看不到。关键是,该环境可能总是发生变化,HoloLens 正在寻找这些更改。在深入探讨了 API,让我们看在实践中的空间映射 (前后,顺便说一下,我没有实际宠物玩具熊)。

若要在操作中查看空间映射,可以连接到 Windows 设备门户在 HoloLens,这将允许远程管理和查看该设备,包括实时的设备所看到的内容的每秒 30 帧视频流。可以运行几乎任何 Windows 10 设备的设备门户。可以访问该方法是︰ 转到设备 IP,或插入设备通过 USB 一旦启用此设置已在开发人员设置 HoloLens 上的 127.0.0.1:10080。在所述,可以为设备门户启用大多数 Windows 10 设备bit.ly/2f0cnfM图 3图 4显示空间网格检索从设备门户中的三维视图。图 3显示就立即将其 HoloLens 所看到的内容,而图 4简要演练一下我起居室后显示的视图。请注意在右边,远墙上插座旁边椅子,因为在以后显示 (在图 9) 时,我问空间了解库,以查找我 sittable 图面。

右方 HoloLens HoloLens 空间网格中的已启用新建聊天室
图 3 HoloLens 空间网格右侧后 HoloLens 新建聊天室中已启用

HoloLens 空间网格后的快速演练费的一部分
图 4 HoloLens 空间网格后的快速演练费的一部分

如何空间映射的工作原理

空间映射通过一个 SurfaceObserver 对象,如您看到的现象图面卷监视新的、 更新和删除曲面。现成的情况下,你需要使用的所有类型都都带有 Unity。尽管 HoloToolkit Unity 存储库 GitHub 上的 HoloLens,包括一些令人惊叹的图面检测,我将介绍一下更高版本,以便此存储库应考虑所必需的命中开始工作了大量功能,则不需要任何其他库。

首先,您告诉 SurfaceObserver,您看到的现象卷︰

public Vector3 Extents = new Vector3(10, 10, 10);
observer = new SurfaceObserver();
// Start from 0,0,0 and fill in a 10 meter cube volume
// as you explore more of that volume area
observer.SetVolumeAsAxisAlignedBox(Vector3.zero,Extents);

更大的区域,越可能发生的计算成本。根据文档中,空间映射扫描在 70 度圆锥 0.8 和 3.1 米为单位之间的区域,大约有 10 英尺扩展 (这些值可能会在将来更改的文档状态)。如果对象较远,不会进行扫描,直到 HoloLens 更接近于它。保持到 0.8 米为单位,以确保用户的手中不会意外地会包含在房间空间网格。

若要将空间数据导入应用程序的过程是,如下所示︰

  1. 通知 SurfaceObserver 要观察的区域的大小和形状 b。
  2. 在预定义时间间隔 (例如每隔 3 秒钟),要求 SurfaceObserver 更新如果您不等待其他要处理的结果。(它是重叠的结果,最好不要; 让一个网格,在处理下一步之前完成。)
  3. 图面观察者可以知道是否添加、 更新或删除外围卷。
  4. 如果添加或更新到已知的空间网格︰
    1. 如果存在此 id,清理旧的图面。
    2. 重复使用 (以节省内存,如果您有一个未被使用的界面),或者分配新 SurfaceObject 与网格、 碰撞体和全体定位组件。
    3. 请将网格数据的异步请求。
  5. 如果删除,删除该卷并使其处于非活动状态,因此您可以重复使用其游戏对象更高版本 (这样可以防止发额外分配和减少垃圾回收)。

若要使用空间的映射,SpatialPerception 是一项必需的功能在通用 Windows 平台 (UWP) 应用程序中。最终用户应注意应用程序可以扫描房间,因为这必须要注意的功能是 Unity 播放器设置中所示图 5,也可以在应用程序的 package.appxmanifest 中手动添加。

在文件生成设置中添加 SpatialPerception
图 5 添加 SpatialPerception 中的文件生成设置

空间网格图面不同于为 SurfaceObserver 观察定义的边界卷的卷中处理。密钥是 SurfaceObserver_OnSurface 委托调用图面卷的更改注释后,请求下一帧中的更改。在一个叫做烤,过程准备然后到网格并 SurfaceObserver_OnDataReady 回调处理网格准备就绪后。

烤通常是指计算内容提前 3D 世界中是一个标准术语。它通常用于谈一谈计算照明信息并传输到使用专门的图像面点过程中调用贴图。Lightmaps 有助于避免运行时计算。烤网格可以采用多个帧从更新函数中要求提供的时间 (请参阅图 6)。性能,网格请求只能从 RequestMeshAsync 上,如果您实际要使用它,否则为您做完了无端额外处理。

图 6 的 Update 函数

private void Update()
{
  // Only do processing if you should be observing.
  // This is a flag that should be turned on or off.
  if (ObserverState == ObserverStates.Running)
  {
    // If you don't have a mesh creation pending but you could
    // schedule a mesh creation now, do it!
    if (surfaceWorkOutstanding == false && surfaceWorkQueue.Count > 0)
    {
      SurfaceData surfaceData = surfaceWorkQueue.Dequeue();
      // If RequestMeshAsync succeeds, then you've scheduled mesh creation.
      // OnDataReady is left out of this demo code, as it performs
      // some basic cleanup and sets some material/shadow settings.
      surfaceWorkOutstanding = observer.RequestMeshAsync(surfaceData,
        SurfaceObserver_OnDataReady);
    }
    // If you don't have any other work to do, and enough time has passed since
    // previous update request, request updates for the spatial mapping data.
    else if (surfaceWorkOutstanding ==
      false && (Time.time - updateTime) >= TimeBetweenUpdates)
    {
      // You could choose a new origin here if you need to scan
      // a new area extending out from the original or make Extents bigger.
      observer.SetVolumeAsAxisAlignedBox(observerOrigin, Extents);
      observer.Update(SurfaceObserver_OnSurfaceChanged);
      updateTime = Time.time;
    }
  }
}
private void SurfaceObserver_OnSurfaceChanged(
  SurfaceId id, SurfaceChange changeType, Bounds bounds, System.DateTime updateTime)
{
  GameObject surface;
  switch (changeType)
  {
    case SurfaceChange.Added:
    case SurfaceChange.Updated:
      // Create (or get existing if updating) object on a custom layer.
      // This creates the new game object to hold a piece
      // of the spatial mesh.
      surface = GetSurfaceObject(id.handle, transform);
 
      // Queue the request for mesh data to be handled later.
      QueueSurfaceDataRequest(id, surface);
      break;
 
    case SurfaceChange.Removed:
      // Remove surface from list.
      // ...
      break;
  }
}

更新代码视为负责获取空间网格任何游戏对象上调用每一帧。

在图面卷烤 RequestMeshAsync 通过请求时,请求将传入每三次方计量器处理的三角形的 SurfaceData 结构可以在其中指定扫描密度 (解决方法)。大于 1000 TrianglesPerCubicMeter 时,您会得到更符合您要扫描的表面的相当平滑结果。另一方面,低三角形计数,性能越好。分辨率为 < 100 是速度非常快,但将失去曲面的详细信息,因此,我建议尝试启动并从那里调整到 500。图 7使用约 500 TrianglesPerCubicMeter。HoloLens 已执行对网格中,某些优化,因此,您将需要与性能测试应用程序并做出决定是否想要扫描和修复更多 (使用更少的内存) 或在较高的分辨率,更容易,但使用更多内存,只需扫描。

虚拟字符检测和坐在实际项目 (从片段应用程序)
图 7 虚拟字符检测和坐在实际项目 (从片段应用程序)

创建空间网格并不 super 高分辨率进程设计使然,因为更高的分辨率等于大得多的处理能力,并且通常不需要与您周围世界交互。您不会使用空间的映射以在您台面上捕获非常详细的小型 figurine — 这是不什么它专为。有很多软件解决方案,不过,通过一种称为 photogrammetry,可用于从图像,如 Microsoft 3D 生成器中,创建三维模型和许多其他列在bit.ly/2fzcH1zbit.ly/1UjAt1e。HoloLens 不包括任何用于扫描和捕获纹理的 3D 模型,但可以查找应用程序以创建三维模型上 HoloLens,如 HoloStudio,或者可以创建它们在 3D 生成器 (或在任何 3D 建模软件就此而言),并使其在 HoloLens 上使用的 Unity。现在还可以在 Unity 5.5 中的新全息版仿真与开发过程中实时流模型从 Unity 中的,以 HoloLens。

在 Unity 中的网格碰撞体是最高性能碰撞体,但它们对于不适合如框和球形基元形状的图面有必要。随着您在图面上添加更多的三角形,并向其中添加网格碰撞体,您可能会影响物理学性能。SurfaceData 的最后一个参数是是否将碰撞体︰

SurfaceData surfaceData = new SurfaceData(id,
            surface.GetComponent<MeshFilter>(),
            surface.GetComponent<WorldAnchor>(),
            surface.GetComponent<MeshCollider>(),
            TrianglesPerCubicMeter,
            bakeCollider);

您可能永远不需要一个碰撞体空间网格上 (并因此传入 bakeCollider = false) 如果您只想要在用户的区域中,检测功能,但不是与物理系统集成。请明智地做出选择。

有许多有关扫描体验的注意事项使用空间的映射。应用程序可能会选择不对其进行扫描、 扫描属于该环境或要求用户为扫描查找特定大小的图面,如沙发上其环境。Windows 开发人员中心的"空间的映射设计"页面上列出了设计准则 (bit.ly/2gDqQQi),并且一定要考虑,尤其是因为还方案到网格中,可能会导致各种瑕疵属于"空间的映射设计"页面上所述的三个常规类别 — 偏置、 hallucinations 和漏洞。一个工作流将要求用户扫描是不够提前,如完成的每个"RoboRaid"会话,以查找游戏以使用适当的图面开头。一旦找到要使用的适用表面,体验启动,并使用提供了这些网格。另一个工作流是扫描最优先考虑,然后扫描不断地按较小的时间间隔,若要查找实际的更改。

使用空间的网格

一旦创建网格后,您可以与之交互以各种方式。如果您使用 HoloToolkit,空间网格已经用一个自定义层属性。在 Unity 中可以忽略或各种操作中包括层。可以全不可见的箭头,在名为 raycast 的常见操作,它将返回它并有选择地指定图层点击碰撞体。

通常我会想要将全息放置在我环境中,在表上,或者甚至类似"Young Conker"(bit.ly/2f4Ci4F),提供要将移动到在现实生活中 (通过空间网格) 要转到选择一个区域来的字符的位置。您需要了解您可以 intersect 与物理世界。中的代码图 8执行到 30 米 raycast 出,但会报告回命中空间的映射网格上的区域。如果它们不是在此层上,将忽略其他全息。

图 8 执行 Raycast

// Do a raycast into the world that will only hit the Spatial Mapping mesh.
var headPosition = Camera.main.transform.position;
var gazeDirection = Camera.main.transform.forward;
 
RaycastHit hitInfo;
// Ensure you specify a length as a best practice. Shorter is better as
// performance hit goes up roughly linearly with length.
if (Physics.Raycast(headPosition, gazeDirection, out hitInfo,
    10.0f, SpatialMappingManager.Instance.LayerMask))
{
  // Move this object to where the raycast hit the Spatial Mapping mesh.
    this.transform.position = hitInfo.point;
 
  // Rotate this object to face the user.
  Quaternion rotation = Camera.main.transform.localRotation;
  rotation.x = 0;
  rotation.z = 0;
  transform.rotation = rotation;
 
}

我不一定要使用空间的网格中,当然。如果我希望才会显示一张全息图和用户能够将其放在任何他想位置 (也许它始终遵循他),而且将永远不会与物理环境集成,我肯定不需要 raycast 或甚至网格碰撞体。

现在让我们来执行某些操作与对网格充满乐趣。我想要尝试确定位置中的区域存在一个字符可以坐下来,我起居室之外,非常类似中的场景图 7,即从"片段",为包含虚拟字符有时坐在房间里 HoloLens 的令人惊叹几乎每周五小时解决神秘的体验。一些我将逐步介绍的代码摘自 HoloToolkit。来自 Asobo Studio,从事"片段"。 由于这混合现实的是太棒了开发体验该组合与虚拟世界中实际就是普通。图 9是从 HoloToolkit 示例的最终结果,我已经在我的卧室中运行的 SpatialUnderstandingExample 场景。请注意,它指示被视为 sittable 区域的多个位置。

HoloToolkit SpatialUnderstanding 功能
图 9 HoloToolkit SpatialUnderstanding 功能

此完整的代码示例在 HoloToolkit,但让我们看一下该过程。我已经适用分成裁减代码。(我已经谈 SurfaceObserver 已以便将从本部分中排除。) SpatialUnderstandingSourceMesh 包装 SurfaceObserver 通过 SpatialMappingObserver 类来处理网格,并将创建相应的 MeshData 对象,要传递给 SpatialUnderstaing DLL。此 API 的主要力量存在于此 DLL 在 HoloToolkit 中。

若要查找的形状使用 DLL 我空间网格中,我必须定义我要查找的自定义形状。如果我想之间 0.2 和偏离地板上,至少一个离散的平面上和 0.2 米的总表面积最小值组成的 0.6 米 sittable 面我可以创建将获取传递给通过 AddShape DLL 的形状定义 (请参阅图 10)。

图 10 创建形状定义

ShapeDefinitions.cs
// A "Sittable" space definition.
shapeComponents = new List<SpatialUnderstandingDllShapes.ShapeComponent>()
{
  new SpatialUnderstandingDllShapes.ShapeComponent(
    new List<SpatialUnderstandingDllShapes.ShapeComponentConstraint>()
    {
      SpatialUnderstandingDllShapes.ShapeComponentConstraint.Create_
        SurfaceHeight_Between(0.2f, 0.6f),
      SpatialUnderstandingDllShapes.ShapeComponentConstraint.Create_
        SurfaceCount_Min(1),
      SpatialUnderstandingDllShapes.ShapeComponentConstraint.Create_
        SurfaceArea_Min(0.20f),
  }),
};
// Tell the DLL about this shape is called Sittable.
AddShape("Sittable", shapeComponents);

接下来,我可以检测这些区域然后实现可视化效果或将游戏对象置于其中。我并不限于询问类型的形状并获取所有人。如果我想,我可以构建到 QueryTopology_FindLargePositionsOnWalls 或 QueryTopology_FindLargestWall,我的查询中所示图 11

图 11 查询为形状

SpaceVisualizer.cs (abbreviated)
 
const int QueryResultMaxCount = 512;
private ShapeResult[] resultsShape = new ShapeResult[QueryResultMaxCount];
public GameObject Beacon;
 
public void FindSittableLocations()
{
  // Pin managed object memory going to native code.
  IntPtr resultsShapePtr =
    SpatialUnderstanding.Instance.UnderstandingDLL.
    PinObject(resultsShape);
   
  // Find the half dimensions of "Sittable" objects via the DLL.
  int shapeCount = SpatialUnderstandingDllShapes.QueryShape_FindShapeHalfDims(
    "Sittable",
    resultsShape.Length, resultsShapePtr);
 
  // Process found results.
  for(int i=0;i<shapeCount;i++)
  {
    // Create a beacon at each "sittable" location.
    Instantiate(Beacon, resultsShape[i].position, Quaternion.identity);
 
    // Log the half bounds of our sittable area.
    Console.WriteLine(resultsShape[i].halfDims.sqrMagnitude < 0.01f) ?
      new Vector3(0.25f, 0.025f, 0.25f) : resultsShape[i].halfDims)
  }
}

使您可以提供条件,如"创建其他对象从 1.5 米"HoloToolkit 中还有解算器︰

List<ObjectPlacementRule> rules =
new List<ObjectPlacementRule>() {
    ObjectPlacementRule.Create_AwayFromOtherObjects(1.5f),
};
// Simplified api for demo purpose – see LevelSolver.cs in the HoloToolkit.
var queryResults = Solver_PlaceObject(....)

在执行前面的查询来放置对象时之后, 您得到结果可用于确定位置、 边界和方向向量,若要查找的曲面的方向的列表︰

public class ObjectPlacementResult
{
  public Vector3 Position;
  public Vector3 HalfDims;
  public Vector3 Forward;
  public Vector3 Right;
  public Vector3 Up;
};

总结

空间的映射可以真正集成与周围世界和从事混合实际体验。您可以引导用户扫描其环境,然后将提供有关新增功能您已经找到以及巧妙地确定其环境,用于进行交互与她接触您全息她反馈。没有其他设备像 HoloLens 混合世界联系起来。签出HoloLens.com并立即开始开发叹为观止的体验。接下来次,我将讨论如何共享体验 HoloLens 上。到那时,保留开发 !


Adam Tuliper是与生活在阳光明媚 SoCal Microsoft 的资深技术推广人员。他是 Web 开发人员/游戏开发人员Pluralsight.com作者和全能技术爱好者。在 Twitter 上了解他︰ @AdamTuliperadamtuliper.com


衷心感谢以下 Microsoft 技术专家对本文的审阅︰Jackson 字段