英特尔® 多核企业 VR 体验:将人群模拟集成到混合现实中

1.简介

开发虚拟现实 (VR) 体验是一段有趣而令人兴奋的旅途,但如果没有适当的工具或帮手,这段旅途将变会得异常艰难。为了提供帮助,英特尔希望制定一份路线图;提供一系列指导和经验教训,帮助缩小虚拟现实世界的差距。为了说明此虚拟现实开发之旅中所需的工具和实践,我们共同承担项目、规划项目、构建项目,当然还有测试项目。

项目要求令人兴奋,提供了一个全新的 VR 集成领域。我们的任务是构建一个 VR 场景,即一个人群量较大的区域。在该场景中,我们利用 CPU 实施多线程人群模拟和路径查找。然后,让人群穿过该场景的时候在聚集点停下来,像购物一样排队;并让他们在那里站几秒钟,然后继续往前走。最后,为了增加复杂性和交互性,我们让其中至少一个聚集点可移动。聚集点移动时,人群流量相应地调整,模拟现实生活中相同的场景。Takeaway 体验将利用图形处理单元 (GPU) 和 CPU 的多个内核将人群模拟和流程集成到虚拟现实中,从而提供一种引人入胜的沉浸式模拟体验。

2.规划项目

项目只有按计划成功执行才算成功。项目赶不上进度,首先是因为规划不当。这看似是一个基本想法,但却是确保应用得以有效开发的核心。即使编写一行代码,首先也要尽量制定详细的计划,并希望在规划过程中逐渐发现问题,以免之后导致出现代价高昂的错误。有句古话说道:“凡事制定计划,凡事按计划行事”,在任何情况下都应如此。知道它会随时间推移不断演进,应仔细规划应用的运行方式;即使首先进行逻辑思考,也会很有帮助。

对于这个项目,我们从基本概念开始,创建人群模拟,在此 VR 体验中使用多个 CPU 内核执行路径查找。这些基本想法将为之后的一切制定蓝图。在思考流程的过程中,我们需要回答一些问题,以便该项目初具雏形。“我们要使用哪些硬件?”“我们要在哪种引擎中进行开发?”“如何获得人群?”“在该环境中人群如何找到自己的方向?”“用户将会获得怎样的体验?”

2.1 工作流程

这些基本问题开始勾勒出工作流程;回答部分关键问题,其他问题也就迎刃而解。例如,选择硬件后,将立即决定我们使用哪个引擎进行开发和渲染。我们将在下一节中对此进行更多讨论,但每种选择和要求都会产生连锁效应,进一步影响该项目。为了将影响降到最小,我们从小处着手,验证基本概念。

2.1.1 从简单开始

为了验证这些想法,我们将项目分解为最简单的形式;在空间中移动方块,并为这些方块创建行为。

blocks moving through space with behavior
图1.最简单的形式:通过行为在空间中移动的方块

The green block represents a gather point
图2.最简单的形式:绿色方块代表聚集点

为了测试多线程和 CPU 负载,我们创建了一个路径查找迭代变量,该变量分配给数字键盘上的加号和减号键。增加迭代会加重 CPU 的工作负载,并显示内核之间的分配。

Creating test scenarios for evaluating multithread implementation
图3.创建用于评估多线程实施的测试场景

2.1.2 增加复杂性

First figures replacing blocks in simple scene
图4.简单场景中替代方块的第一批人物

Issues with random meshes not aligning properly
图5.随机网格未准确对齐的问题

我们将生成的角色引入方块场景中,以此来增加复杂性。用新的角色替换移动方块,可展示如何随机生成角色,并显示需通过网格对齐来解决的问题。

3.了解您的硬件

硬件可定义项目,因此从一开始就必须确定目标硬件以及可以忽略的硬件。这些选择会影响项目的整个流程。

3.1 Windows* 混合现实

Mixed Reality in each available brand
图6.各个品牌的混合现实

在本示例中,首选平台是 Windows* 混合现实,选择它可帮助我们快速定义开发环境,因为只有 Unity* 支持 Windows 混合现实开发。Windows 混合现实 (Windows MR) 有多种品牌可供选择。

Intel NUC

虽然每个品牌都有自己的优势和重点,但都建立在同一平台上,而且在开发方面基本相同。面向一种开发就可以面向所有开发。事实证明,这对我们的项目来说是一个卖点,并进一步构建了选择 Windows MR 的示例。我们决定尝试使用全新的英特尔® NUC 8 VR 机器,因为它使用的是最新英特尔® 酷睿™ i7-8809G 处理器。

3.2 英特尔® NUC 8 VR 机器

混合现实 (MR)、VR 和基于位置实时渲染的 3D 在所有方面通常都要求使用最新最好的设备,核心计算机也不例外。对于这些媒介来说,渲染功能通常位于 GPU 之中,只有一小部分负载由 CPU 承担。然而对于这一项目,将人群模拟添加到 GPU 密集型 VR 场景也需要 CPU 功能达到最佳,而且是理想中的小尺寸。随着最新英特尔酷睿 i7-8809G 处理器的实施,我们发现,VR 就绪型英特尔® NUC 具有令人惊叹的功能,正好能够满足我们的项目需求。如欲了解有关英特尔 NUC 8 VR 的更多信息;请参阅英特尔 VR 机器简介

4.Unity* 中的混合现实

Mixed Reality Made in Unity
图8.在 Unity* 中创建的混合现实

如前所述,选择 Windows MR 平台作为我们的目标设备立即确定了 Unity 用作开发平台。这是一个很好的选择,因为 Unity 越来越擅长处理 VR 场景,并支持所有级别的用户访问。Unity 需要进行一些编程,Unity微软还提供了大量教程以供参考。目前,Unity 支持所有应用广泛的 VR、增强现实 (AR),MR 和扩展现实 (XR) 设备。有了所有可用的支持后,又回到了为何继续选择和使用 Windows MR 设备的问题;这款设备能够提供哪些优势功能供我们的项目使用?它是这一前瞻性项目的最佳选择吗?

4.1 为何选择混合现实

Lenovo Mixed Reality
图9.联想混合现实

开发此项目时,Windows MR 仍然是 VR 领域相对较新的工具集。它的优势在于设置超快、硬件配置简单、分辨率极高,且开销最小。我们最喜欢的功能是到它目前为止没有灯塔,当今市场上的其他所有设备几乎都使用灯塔。Windows MR 使用基于 Microsoft Kinect* 的计算机视觉技术和类似的摄像头来实际扫描和解析用户周围的环境。通过在 Windows MR 设备上解析这些环境,寻找边缘、平面、地板等,能够确定其在 3D 空间中的位置 — 头戴式显示器的倾斜、偏航和旋转。这是 VR 领域的一次革命,使头显成为项目的完美选择。

4.2 独特要求

使用 Unity 和 Windows MR 设备进行开发的最快途径是通过 GitHub* 提供的 Windows MR 工具套件。在 Unity 中使用插件可加快部署速度,但应该注意的是,在构建和编译项目以便交付给 Windows MR 设备时,Windows MR 支持的唯一交付方法是通用 Windows 平台 (UWP)。UWP 有自身的局限性,因为该项目的一个要求是显示 CPU 负载和内核数,但由于平台的安全功能,UWP 无法从操作系统收集信息。为了解决这个问题,我们创建了一个外部实用程序,用于从 CPU 收集信息,用我们的 UWP 应用启动该实用程序,然后与实用程序本身通信,以不干扰高级安全特性的方式将数据以文本形式传递到框架中。

4.3 控制

Windows MR 设备还使用蓝牙® 技术控制器进行交互,这同样也是 VR 领域极具前瞻性的开发。通过使用蓝牙技术,设置将变得超级简单,延迟几乎为零。最初,我们面临的挑战是如何让各种测试机器始终看到和使用蓝牙技术控制器,我们还发现旧的蓝牙技术适配器不像现有的那样高效可靠。如果您遇到这种情况,可能需要升级。

4.4 面向混合现实设备构建

确定目标设备后,我们的任务将变成面向(而非围绕)该设备进行开发。开始开发时,请查看微软的混合现实开发简介。首先安装混合现实工具

使用 Windows MR 设备时,与其他新设备一样,有时候很难找到相关信息和测试用例。值得庆幸的是,未来将有更多信息以供参考和使用。尽管如此,收藏开发人员网站可帮助快速搜索其他人的经验,以确定最佳的前进方向;请参阅混合现实开发人员网站

5.角色动画

将人群模拟添加到任何项目都会立即产生一个问题,即人群来自何处,或者更糟糕的是,“我们如何制造人群?”解决方案有多种,包括自己创建角色和使用工具为您生成角色。通常使用预制元素,这样您可以在自己的项目中随意使用这些元素。

5.1 使用预制角色

预制角色已存在多年,从 Smith Micro Poser 3D* 软件等开创性高分辨率程序,到 Autodesk Character Generator* 软件等基于 Web 的产品。除非你要寻找一些非常具体的东西,否则没有必要自己创建和绘制角色。使用角色生成工具可节省大量时间,输出的结果也非常理想。经常使用它们需要跳出固有思维模式,以使它们按照我们想要的预期运行。

5.2 使用角色生成工具

针对我们的项目,我们使用默认的 biped 角色在 Autodesk 3ds Max* 软件中完成角色动画。角色模型使用 Autodesk Character Generator* 软件(总共六个角色)创建,并以 FBX 格式导入 Autodesk 3ds Max 软件。

Characters available in the Autodesk Character Generator
图10.Autodesk Character Generator* 软件中可用的角色示例

这些模型带有骨骼系统,但被 Autodesk 3ds Max 软件中的默认 biped 系统所取代,以便轻松地手动绘制动画,或创建简便的 *.bip 文件加载方法。

Artist representation of VR
图11.VR 艺术表现

将 biped 系统蒙皮至角色网格一次,并复用到下一个模型中,因为它们都使用相同的网格拓扑。动画由两种不同的动画状态组成,一种用于步行周期,另一种用于站立姿势。完成蒙皮和动画后,每个模型都以 FBX 格式导回至 Unity。

The model after importing to Autodesk 3ds Max
图12.导入至 Autodesk* 3ds Max 软件(左),Autodesk 3ds Max 软件默认 biped 系统(中)的模型以及经过蒙皮和绘制动画的角色(右)

5.3 实施过程中面临的挑战

虽然预制角色很好,但通常不会设置或操纵它们以完全按照您的项目要求进行操作。在我们的示例中,我们需要随机生成的人群,由躯干、头部和腿部混合而成的,成为一个独特的人物样本。为此,我们将原始网格分为三个不同的部分(头部,躯干和腿部),验证所有部分都可以在模型之间切换,并尽可能地相匹配,以避免混合时出现任何问题或遗漏。此外,我们为每个身体部位创建三种不同的纹理,以增加组合的随机数。完成所有操作后,每个完整的模型(头部,躯干和腿部)以 FBX 格式导出至 Unity,纹理包以 JPG 格式单独导出。

使用的软件:Autodesk 3ds Max 软件、Autodesk Character Generator 软件和 Adobe Photoshop* 软件。

6.环境选择

VR 比其他媒介更需要关注环境。当玩家沉浸其中时,将一种环境替换为另一种环境会变得越来越困难。有时为了产生戏剧性效果或创造某种体验感,可以这样做。但在这个特殊项目中,我们一直在寻找一种人群实际形成的环境,在这种环境中他们会以某种方式行事,修改他们的流程模式也是有意义的。我们评估了火车站、购物中心、机场和体育场馆。每种环境都有自己的创作挑战,其中最大的挑战是创建完全自定义环境所需的时间。为了最大限度地减轻这种负担,我们寻求合作伙伴和供应商为我们提供预制环境,我们针对项目对预制环境进行构建和完善。

6.1 预制和定制

是创建自己的对象和环境,还是寻找现成可用的对象和环境,是所有项目都必须面对的问题。使用预定义素材可以节省大量的时间和精力,因为从头开始构建是一项非常艰巨的任务。是的,可以完成,但为了项目的利益,从预定义的环境开始为我们创造了最大的机会,因为我们可以专注于定制该环境,让其展示我们需要展示的内容。为此,我们采用了比较混合的方法。

6.2 混合方法

Unity Asset Store* 提供预制环境,而且其环境由多个部分组成,我们可以组合搭配以建立一种真正的自定义感觉。有许多免费的简单版本,还有昂贵的完整工具套件,支持更多的定制,无需为每种外观或元素建模。对于我们的项目,我们从位于德克萨斯州达拉斯的美国航空中心开始。环境最初由 InContext Solutions 以模拟的形式建模,以展示零售广告投放。由于此项目的范围,我们进行了一些建模优化和纹理修改以适应项目要求,并添加了光照和后期处理效果。

Example of Prefabricated Environment
图13.预制环境示例

此外,也为内部的自定义元素进行了建模,以更紧密地匹配场景,并为每个行走角色的生成点提供一个在屏幕外存在的位置。

作为该项目的一部分,InContext 让我们用它来展示如何采用混合方法构建环境。作出这样的选择,由于其位置非常引人注目,因此必须得到许可和批准才能开始使用。

American Airlines Center
图14.美国航空中心

6.3 许可

许可是项目的关键要素。无论使用第三方库、图像、模型,还是其他人创建,甚至看起来像其他人的东西,都必须取得相应的权限。在本示例中,我们使用的是美国航空体育馆的形象,因此我们必须联系他们的团队,以验证我们是否有权展示他们的形象、使用的方式以及使用的目的。我们通过电子邮件通信、反复讨论并最终签订合同,获得了许可,项目得以继续进行。

7.材料

体验是好是坏,材料是关键,因为材料能够赋予网格生命力。然后我们为这些材料提供光照,灯光产生阴影和反射,也有助于让人相信眼睛看到的东西是真实的。我们稍微了解一下光照,因为材料我们要用一个章节来进行介绍。创建材料并定义如何将其用于模型时,必须首先确定我们所使用的渲染方法。最重要、最简单的区分是实时渲染和离线渲染之间的区分。离线渲染是指场景、动画、灯光等都在渲染之前完成,然后在渲染批处理中执行。大多数动画电影和预渲染材料都采用这种方式创建。另一种是实时渲染。实时渲染顾名思义,是指渲染实时发生。如果用户在 VR 中看向左侧,计算机确定用户在一个方向上朝每个度数移动头部时将看到的确切内容。实际上,计算机为每帧进行两次计算(每只眼睛一次),帧速为每秒 90 帧。渲染量非常大!因此,VR 需要采用特定类型的材料,并高速执行材料处理。

Materials in Unity
图15.Unity 中的材料

7.1 VR 和材料

VR 的性能与 GPU 需提取数据并将数据绘制到屏幕的次数有着直接的关系。绘制调用指绘制到屏幕中的对象数量、对象如何调用数据,以及从何处调用;请参阅 什么是绘制调用?绘制调用次数多直接影响对象的性能,但对于 VR、AR、MR 以及其他类型的实时渲染沉浸式 3D 来说,这一点更为重要。为什么?因为这些体验依赖于最小、有时最简单的图形处理器(例如 Microsoft HoloLens*),而且为了准确执行,它们必须保持一定的帧速率。为了优化性能,通过支持这种目标帧速率,我们创建复合网格,使网格数量尽可能的少,并让它们共享相同的材料。复合网格共享相同的材料称为使用纹理图集或纹理包。创建纹理图集是开发 VR、MR 和 AR 体验过程中的关键步骤,而且它是 Unity 的内置功能;请参阅有关 Texture2D 的 Unity 文档。还有使用许多其他的工具通过多种方式实现此目的,但对于本项目,我们使用 Autodesk 3ds Max 软件优化工具,以减少绘制调用。

Unoptimized stadium scene
图16.未经优化的体育场馆,绘制调用次数较多(突出显示)

Scene optimized showing reduction in draw calls
图17.场景经过优化,显示绘制调用次数有所减少

7.2 Unity 和材料

Unity 支持使用不同类型的材料。其中一些较为复杂,但在此特殊项目中,我们主要将 Unity 标准材料用于体育馆,将自定义双面材料用于角色(以避免混合身体部位时出现遗漏)。体育馆材料使用默认高光设置,以便大部分都能使用反照率、法线、高光和自发光贴图。针对角色我们仅使用反照率贴图。

Example of Unity Shader setup
图18.Unity* 着色器设置示例

8.光照

Example of area lights
图19.Unity.com 提供的区域光源示例

前面我们详细介绍了材料,现在我们来看看如何显示这些材料,那就是通过光照。光照是光线在材料和物体上的虚拟投影,导致光线反射、消除或完全阻挡。在离线渲染中,光照过程需要花费大量时间进行设置,但也可以在最终渲染之前的任何时间进行调整。为了让我们能容易理解光照过程,应用使用传统名称来处理大多数光源,并尽最大努力使这些虚拟灯光像实际灯光那样发挥作用。我们瞄准并指向这些灯光,以产生明显的效果。在实时项目中,光照过程特别困难,因为主要目的是在不影响性能的情况下,实现非常逼真的外观。在这个过程中总要作出一些让步,因此必须了解如何设置光照、全局照明 (GI) 以及如何在 Unity 内烘焙光照等信息。更多有关光照的信息,请参阅 数字动画 3D 光照技术指南

8.1 Unity GI

全局照明 (GI) 在光照过程中非常重要,因为它可以提高数字场景的真实感。GI 计算光的反射及其如何将能量传递到远离直射光的表面,从而形成自然逼真的视觉效果。GI 的问题在于其计算量很大,因此大多数情况下,实时进行这种计算并不可行。为了解决这个限制,我们将 GI 结果烘焙到纹理中,以降低对性能的影响。将光烘焙到纹理这一过程可以在 Autodesk 3ds Max 软件等程序中完成,也可以在每次发布项目时在 Unity 中完成。为不同的平台创建设置时,Unity 支持精细地控制光照的烘焙方式;能否创建性能出色的游戏或体验,性能管理的各个方面都极为关键。

without G I
图20.未使用 GI

with G I
图21.使用 GI

在项目中,我们通过在体育场模型中的灯光或照明位置创建一组灯光,以应用这些 GI 想法。为这些对象分配自发光材料后,我们开始光照贴图过程计算,其中包括 GI 的计算、环境遮挡,以及它们如何与材料和纹理交互以达到逼真的效果。

为了在 Unity 中做到这点,我们需要使用光照贴图 UV(用于存储光照贴图)设置所有几何体,这些几何体将成为光照贴图计算的一部分,并将其定义为静态网格。这意味着所有这些几何体都不会移动,并且光可以毫无问题地进行存储。如果是动画对象,那么这些操作无效。之后,只需调整部分技术参数并等待计算完成。一切都完成后,所有的灯光都可以禁用,一切都应该看起来一样,不需要打开多个灯光,影响性能。详细了解 Unity 如何处理光照贴图

8.2 平衡性能和观感

平衡性能是所有实时开发人员都需要面对的主要任务之一,但在 VR 开发过程中,这一点更为重要。VR 头显的技术规格低于标准工作站,因此性能是决定游戏质量的关键。因此,无论您在 Unity 中创建的项目多么逼真或美观,如果在所选平台上表现不佳(VR 头显中每秒 90 帧),那么对用户来说它将不会是一次愉快的体验。

后期处理(有时简称为后期)可以帮助实现令人惊叹的效果,但需要付出代价。后期处理是指在渲染(处理)后(后期)应用于场景的效果和变化。例如,光照效果、大气、色彩校正、色彩混合、屏幕空间反射等。虽然我们能够在项目中使用后期处理,但必须谨慎行事,并在每次启闭时进行测试,以确保性能不受影响。即使如此,我们仍然喜欢启用屏幕空间反射时的场景外观,但最终必须禁用它们,以保持帧速率。

Scene without post processing
图22.Unity.com 中没有后期处理的场景

Scene with post processing
图23.Unity.com 中进行过后期处理的场景

8.3 我们遇到的挑战

我们遇到的主要挑战是与 VR 相关的技术限制。最新、最棒的设备可能非常先进,但在可以同时显示和计算的内容方面,将始终受限。屏幕空间反射就很好地说明了这一点。它们可以帮助在 Unity 编辑器中产生令人惊叹的效果和观感,但添加这种效果会导致性能降到理想标准以下。我们选择禁用它,并使用更基本的反射技术(光照探头),这些技术不那么逼真,但仍然会将类似的效果传达给大众,同时不会对性能造成太大影响。

Basic reflections using reflection probes
图24.使用反射探头的基本反射图像

Image with complex reflections
图25.反射复杂的图像(屏幕空间反射启用)

9.人群模拟人工智能

人群模拟是处理器密集型最高的功能之一,在性能至关重要的情况下,在 VR 内部提出了一系列独特的挑战。为了充分利用英特尔® CPU 功能和适用于多线程的库,我们将人群模拟路径查找移至所有可用内核中,分配负载并实现我们所需的性能。不过,我们首先要解决一个问题:是自己构建还是使用可用库?值得庆幸的是,我们可以使用一款众所周知的解决方案。

9.1 Recast 和 detour

extern "C" {
	// NavMeshAgent
	dtCrowdAgent* NavMeshAgent_Create(float pos[3], float radius)
	{
		dtCrowd* crowd = sample.getCrowd();

		dtCrowdAgentParams ap;
		memset(&ap, 0, sizeof(ap));
		ap.radius = radius;
		ap.height = ap.radius * 2.0f;
		ap.maxAcceleration = 8.0f;
		ap.maxSpeed = 3.5f;
		ap.collisionQueryRange = ap.radius * 12.0f;
		ap.pathOptimizationRange = ap.radius * 30.0f;
		ap.updateFlags = 0;
		ap.updateFlags |= DT_CROWD_ANTICIPATE_TURNS;
		ap.updateFlags |= DT_CROWD_OPTIMIZE_VIS;
		ap.updateFlags |= DT_CROWD_OPTIMIZE_TOPO;
		ap.updateFlags |= DT_CROWD_OBSTACLE_AVOIDANCE;
		ap.updateFlags |= DT_CROWD_SEPARATION;
		ap.obstacleAvoidanceType = 3;
		ap.separationWeight = 2;

		int idx = crowd->addAgent(pos, &ap);

		dtCrowdAgent* agent = crowd->getAgent(idx);

		dtAssert(agent);

		return agent;
	}

代码 1.这里通过固定规则生成代理。

Recast/Detour 是一种先进的导航网格构造和游戏路径查找工具集。它非常有名,广泛应用于 Unreal Engine*、Unity 等引擎。该库用 C/C++ 编写,可以帮助大多数硬件释放最佳性能,并提供最高的灵活性。虽然其 API 非常简单,但也可以源代码的形式下载该库,并进行修改,以满足您的具体需求。默认情况下,路径查找算法是单线程,尽管需要进行一些配置才能启用,但该库的许多部分都是为多线程设置的。

该库分为两个部分 — Recast,专门用于导航网格构建,以及 Detour,它是我们用于查找路径的空间推理工具套件。这两个方面都可用于创建我们的人群模拟。Recast 在运行时创建 NavMeshes,Detour 通过这些网格找到路。在我们的具体项目中,场景地形是一个 IntelNavMesh,通过物体找到路的角色为 IntelNavMeshAgent,并且动态障碍物(如热狗车)被称为 IntelNavMeshObstacle。每个 NavMesh 都会分配一个成本,我们只需要 NavMesh 根据特定的起点和终点返回路径。根据此请求,NavMesh 代理会按照预定义的规则识别所有逻辑路径,并返回它们:

path = NavMesh.PleaseGivePath(FromA, ToB);

总的来说,Recast/Detour 是一款高度动态的工具,拥有充分的支持和丰富的文档。提供所有模块以及深入的示例

// Integrate.
parallel_for(0, nagents, [&](int i)
{
	dtCrowdAgent* ag = agents[i];
	if (ag->state != DT_CROWDAGENT_STATE_WALKING)
		return;

	integrate(ag, dt);
});

代码 2.集成

// Find nearest point on navmesh for agents and request move to that location
parallel_for(0, crowd->getAgentCount(), [&](int j)
{
	dtCrowdAgent* ag = crowd->getAgent(j);

	if (ag->active && ag->bHaveValidDestination)
	{
		dtPolyRef targetPolyRef = 0;
		float targetPos[3];
		navquery->findNearestPoly(ag->destination, halfExtents, filter, &targetPolyRef, targetPos);

		crowd->requestMoveTarget(j, targetPolyRef, targetPos);
		ag->bHaveValidDestination = false;
	}
});

代码 3.查找最近的点示例

// Get nearby navmesh segments and agents to collide with.
parallel_for(blocked_range<int>(0, nagents), [&](const blocked_range<int>& r)
{
	// static nodePool for each thread, deallocated at program exit
	if (gNodePool == nullptr)
	{
		auto deleter = [](dtNodePool* pool) {pool->~dtNodePool(); dtFree(pool); };
		gNodePool = unique_ptr_deleter<dtNodePool>(new (dtAlloc(sizeof(dtNodePool), DT_ALLOC_PERM)) dtNodePool(64, 32), deleter);
	}

	for (int i = r.begin(); i < r.end(); ++i)
	{
		dtCrowdAgent* ag = agents[i];
		if (ag->state != DT_CROWDAGENT_STATE_WALKING)
			continue;

		// Update the collision boundary after certain distance has been passed or if it has become invalid.
		const float updateThr = ag->params.collisionQueryRange*0.25f;
		if (dtVdist2DSqr(ag->npos, ag->boundary.getCenter()) > dtSqr(updateThr) || !ag->boundary.isValid(m_navquery, &m_filters[ag->params.queryFilterType]))
		{
			ag->boundary.update(ag->corridor.getFirstPoly(), ag->npos, ag->params.collisionQueryRange, m_navquery, &m_filters[ag->params.queryFilterType], gNodePool.get());
		}

		// Query neighbour agents
		ag->nneis = getNeighbours(ag->npos, ag->params.height, ag->params.collisionQueryRange, ag, ag->neis, DT_CROWDAGENT_MAX_NEIGHBOURS, agents, nagents, m_grid);

		for (int j = 0; j < ag->nneis; j++)
			ag->neis[j].idx = getAgentIndex(agents[ag->neis[j].idx]);
	}
});

代码 4.识别附近碰撞器周围的路径。

 

10.利用多 CPU 内核

将路径查找计算分布于所有可用内核不仅是项目的要求,还是执行处理器密集型操作的最佳实践。开发(尤其是 VR 开发)的另一个巨大挑战是如何利用所有可用的资源为用户创建最佳体验。多线程应用可以实现这一目标,而且更高效、更快速,使用正确的工具实施起来非常简单。

10.1 英特尔® 线程构建模块

英特尔提供一个名为英特尔® 线程构建模块(英特尔® TBB)的库,支持轻松使用多个内核执行几乎每一个进程。它免费提供,而且易于实施。对我们的项目来说,英特尔® TBB 与 Recast&Detour 一起实施,以对库进行扩展,进而将路径查找移到所有可用内核中。路径查找属于处理器密集型操作,而且能够从多线程方法中受益。虽然英特尔® TBB 是一种非常强大的库,打开了一个与各内核交互的全新世界,但对于执行,我们能够在默认配置下实施并立即捕获所需的结果。

11.面向 VR 的 UX/UI

用户体验 (UX) 和用户界面 (UI) 开发本身就是一门艺术。随着应用和机器成为我们日常生活不可或缺的一部分,UI/UX 开发人员的工作就是创建一种直观、顺畅的方式支持我们与应用或机器进行交互,使交互不会成为体验的阻碍,而是能够增强体验。VR 重写了 UI/UX 的规则手册,基本原则相同,但实现原则的工具大相径庭。

11.1 局限性

沉浸在 VR 体验中时,通常没有漂在半空中的按钮、菜单或用户界面。是的,的确如此,但有时使用这样的系统可以非常直观,而且非常有趣。然而,这样的系统通常不能移动,要求用户处于静止位置。使用 VR,用户可以向各个方向移动,可以转头、蹲下、看到自己的手等等。关键是开发一种融入用户熟悉的日常手势和动作的 UI/UX。

11.2 我们的选择

由于使用 Windows MR,我们定义了一种按钮配置。必须使用以前开发的 Windows 软件来确定 Microsoft 打算用来使用其控制器的方法。即使如此,触发按钮和拇指操纵杆的存在为交互提供了大量机会,其中大部分交互都感觉非常真实。因此,我们希望尽可能模仿自然移动,支持用户通过触摸控制器和挤压触发器来抓取物体。然后,用户可以按住触发器来握住该物体,并在释放触发器时将物体释放出去。它将成为 VR 体验的标准,经过证明它仍然是使用 Windows MR 的最佳途径。导航选项由 Windows MR 的主屏幕中的同一个导航控件来定义。我们的目标是尽可能无缝地实现过渡。

12.打造沉浸式 VR

现在,抓取并与物体交互的元素、完全的空间移动性等所有准备工作都已就绪,我们可以开始创建令人惊叹的视觉体验,但仍然感觉缺少一些东西。它就是声音设计 — 体验开发过程中一个经常被忽视的领域。

12.1 声音、2D 和 3D

声音设计指使用不同的声音来反映做出相同动作、处于相同位置或与类似设备或对象交互的真实体验。通常称之为声音效果,但总体过程比这更全面。为了在该项目上实施声音设计,我们仔细考虑了类似环境中较差的体验。可能会有 2D 声音和 3D 声音,2D 声音不会随用户转动或变换位置而改变,3D 声音表示声源。3D 声音会根据用户与声源的相对位置在音量或立体声位置上发生变化。为了简单起见,我们使用其中的一种。2D 声音是环境声;人们在空间中走来走去的声音。用户在场景中移动时,这种声音始终保持一致。一开始声音随着人群进入房间而逐渐增大,在达到满音量后保持均匀的音量和立体声图像。3D 声音是来自多个运动场的声音,分成若干个小片段,与商场和音乐厅中的人群噪音混合在一起。最终的声音循环长度超过两分钟,旨在无形地循环到用户耳中。声音从相邻体育场的篮球场中心发出,当你接近入口时音量增加,当你走远或转身时音量减小。

3D sound looped in Unity
图26.置于项目场馆内的 3D 声音,在 Unity* 中用作声源和循环

12.2 测试、测试、再测试

沉浸式虚拟现实体验可以编写、开发和介绍,但用户只有实际佩戴头盔进入体验之后,才能知晓如何分辨事物。因此,测试是获得最佳体验的关键。测试,测试,再测试,调整每个吸引眼球或在体验中会破坏现实幻想的元素。随着更多用户参与体验,将会提供更多可用数据,哪些有效,哪些无效,所有的问题都会自行显露出来。值得庆幸的是,这些问题是可以解决的,随着问题的解决,虚拟现实体验将得到进一步改善。

13.总结

本文一开始,我们宽泛地定义项目,引出几个关于如何创建 VR 体验的问题。首先大致回答每个问题,然后通过规划过程打磨每一个问题的答案,这样我们才能解决项目中不可避免的一些麻烦和失误。问题仍然存在,但通过规划过程能够最大限度地减少问题。

规划之后,我们以最简单的形式创建了应用,以验证概念并解决部分难题,例如人群模拟和简单的多核开发,使我们能够最大限度地减少变量,并专注于特定的开发领域。随着时间的推移和部分问题的解决,我们增加了复杂性并融入角色,让它们随机切换,并评估是否成功。然后,我们在环境中添加了材料和光照,以确保在达到性能基准的同时,实现相应的观感。结合所有元素,我们努力更好地集成每一个正在构建或改进的部分。我们拨入用户体验和界面,以确保人们直观地了解如何使用我们的体验。我们通过测试、测试,再测试,添加了声音并专注于实现沉浸式体验。最后,我们实施通过测试流程获得的所有用户数据,纠正明显的问题,实现了我们的目标。现在,我们的 VR 项目符合项目要求,采用的所有技术都符合逻辑和易于理解,同时该项目还具有很大的娱乐性。

有关编译器优化的更完整信息,请参阅优化通知