手部跟踪 - MRTK3

概述

铰接式手部关节数据是尚未由 Unity 输入系统本地处理的输入数据片段之一,由子系统处理。

注意

如果你不熟悉 MRTK3 子系统及其与 MRTK 2.x 服务的区别,请参阅 MRTK3 子系统体系结构文档,深入了解我们的理念和设计。

我们的子系统引入来自多个源的手部关节数据,并将它们聚合到一个可跨设备和模拟环境工作的中央 API 中。 以下子系统是 HandsSubsystem 的实现:

  • OpenXRHandsSubsystem 直接从 OpenXR 插件接收手部数据。
  • XRSDKHandsSubsystem 从 Unity 的 XR SDK 抽象层接收手部数据(反过来,它可能从 OpenXR 或一些其他来源获取数据)。
  • SyntheticHandsSubsystem 根据来自系统的输入动作(例如 devicePositiondeviceRotation 等)合成假的手部关节。 该子系统提供在编辑器中使用输入模拟时看到的关节。

HandsAggregatorSubsystem 是一个子系统,它将所有手部数据的源合并到中央 API 中。 它从所有主动运行的 HandsSubsystems 中提取骨骼关节数据。 HandsAggregatorSubsystem 的 MRTK 实现是 MRTKHandsAggregatorSubsystem,支持每帧手部数据的延迟加载和重用。

重要

每当直接查询手部关节数据时,始终从聚合器查询,而不是从任何单个手部子系统查询。 这样,代码将适用于任何手部数据源,包括模拟数据。

聚合器和手部子系统徐缓地评估传入的手部数据请求。 在“客户端”脚本请求之前,不会查询手部数据。 如果应用仅请求单个关节,则手部子系统将徐缓地评估并且仅从基础 API 查询单个关节。 此外,如果“客户端”请求完整的关节数据,同一帧内的后续调用将重用相同的数据,从而减少在同一帧内查询多个关节的成本。 在每个新帧上,缓存将被弄脏并刷新,后续调用将开始重新填充缓存。

因此,在分析应用程序时,你可能会发现帧中的第一个关节查询比后续查询花费更多时间。 这是由于与第一个查询关联的摊销成本,以及后续“缓存命中”的相对性能。

捏合特征

聚合器根据从每个特定手子部系统查询的关节数据计算有关捏合手势的多个测量值。 这些测量值在聚合器子系统配置中进行配置。

Hands Aggregator subsystem configuration

“捏合开放阈值”和“捏合封闭阈值”控制拇指和食指之间的绝对世界距离,用于规范化捏合进度。 当距离等于封闭阈值时,捏合进度为 1.0,当距离等于开放阈值时,为 0.0。 (这些阈值目前采用世界单位,但很快就会规范化为用户的手部大小。)

“举手相机 FOV”控制手必须离用户视野中心多近才能被视为有效捏合。 “手背向容差”控制测量用户手部旋转的容差,以确定用户的手何时背向

手部聚合器示例

// Get a reference to the aggregator.
var aggregator = XRSubsystemHelpers.GetFirstRunningSubsystem<HandsAggregatorSubsystem>();
// Wait until an aggregator is available.
IEnumerator EnableWhenSubsystemAvailable()
{
    yield return new WaitUntil(() => XRSubsystemHelpers.GetFirstRunningSubsystem<HandsAggregatorSubsystem>() != null);
    GoAhead();
}
// Get a single joint (Index tip, on left hand, for example)
bool jointIsValid = aggregator.TryGetJoint(TrackedHandJoint.IndexTip, XRNode.LeftHand, out HandJointPose jointPose);
// Get an entire hand's worth of joints from the left hand.
bool allJointsAreValid = aggregator.TryGetEntireHand(XRNode.LeftHand, out IReadOnlyList<HandJointPose> joints)
// Check whether the user's left hand is facing away (commonly used to check "aim" intent)
// This is adjustable with the HandFacingAwayTolerance option in the Aggregator configuration.
// "handIsValid" represents whether there was valid hand data in the first place!
bool handIsValid = aggregator.TryGetPalmFacingAway(XRNode.LeftHand, out bool isLeftPalmFacingAway)
// Query pinch characteristics from the left hand.
// pinchAmount is [0,1], normalized to the open/closed thresholds specified in the Aggregator configuration.
// "isReadyToPinch" is adjusted with the HandRaiseCameraFOV and HandFacingAwayTolerance settings in the configuration.
bool handIsValid = aggregator.TryGetPinchProgress(XRNode.LeftHand, out bool isReadyToPinch, out bool isPinching, out float pinchAmount)

手部控制器预制件

MRTK LeftHand ControllerMRTK RightHand Controller 预制件使您能够在项目中使用手部控制器。 这些预制件可以与关节式和非关节式的手部控制器一起工作。 他们具有戳击、抓取、远程射线和注视夹取操作的交互器。 此外,这些预制件显示适当的视觉效果,并使用附加到它们的控制器和交互器组件处理来自设备的输入操作。 这些组件反过来又使用 Unity 的输入操作映射,这些映射声明了输入绑定。 默认情况下,预制件使用包含的 MRTK Default Input Actions 资产中定义的输入操作映射。

请注意,手部控制器预制件已包含在 MRTK XR Rig 预制件中,请参阅创建新场景以获取更多详细信息。 如果 MRTK3 的 XR Rig 已被使用,则无需采取进一步操作来支持手部控制器。

The hierarchy of the MRTK LeftHand Controller Unity prefab.

重要

如果修改了 MRTK3 的 XR Rig 上的输入操作并使用 MRTK Default Input Actions 资产外部定义的操作,请务必更新 MRT3 的输入操作管理器,以便其指向新的输入操作资产。 不这样做则会导致未定义的行为。

MRTK3 的左手和右手控制器预制件包含支持手部输入必需的所有组件。 其中一个这样的组件是 MRTK 的 ArticulatedHandController,它是 Unity XR 输入的一个 ActionBasedController 专用版本。 此 MRTK3 控制器脚本使用 MRTK3 的手部聚合器子系统,以便公开各种手部输入事件。 例如,该控制器会公开夹取选择事件。

手部预制件还具有用于启用手部控制器可视化的脚本。 HandJointVisualizer 组件用于调试,并在每个手部关节上绘制实例化网格。 虽然 ControllerVisualizer 组件适用于生产场景,但当检测到控制器时,它会呈现相应的控制器模型。 将在平台的控制器网格可用加以使用,否则将使用在 fallbackControllerModel 字段上指定的泛型控制器模型。

非 HoloLens 平台可以使用 RiggedHandMeshVisualizer 来可视化手部。 正如名称所示,此可视化工具使用操纵网格来呈现高质量的手部模型,由于性能和设计原因,不建议用于 HoloLens 等 AR 平台。 请注意,此可视化工具未配置在默认手部控制器预制件上。

注意

对于 HoloLens 等增强现实平台,我们建议不要使用任何手部可视化效果。 用户真实的手与稍微延迟的全息可视化之间的冲突可能会比其具有的价值更令人分心。 但是,对于不透明的平台,这是一个很好的解决方案。

默认手部控制器预制件还托管了一组交互器组件。 其中包括 PokeInteractorMRTKRayInteractorGrabInteractorGrabInteractor。 有关这些交互器组件的详细信息,请访问交互器体系结构 — MRTK3

最后,控制器预制件也包含检测器组件,IInteractionModeDetectorNearInteractionModeDetector 以及 InteractionDetector。 这些组件通知应用程序的交互模式管理器应启用哪些交互器组件。 有关 MRTK3 检测器的详细信息,请访问交互模式管理器 — MRTK3

姿势源

所有 MRTK3 的手部交互器都需要一个控制器姿势(或手部姿势)。 手部姿势的类型可能因交互器而有所差异。 例如,一些使用 Unity 输入操作来获取位置和旋转,而另一些则使用食指姿势,还有一些使用手掌姿势。 每个交互器的手部姿势源由实现 MRTK3 的 IPoseSource 接口的类定义。 此接口声明以下内容:

  • TryGetPose。 此方法尝试在全局空间中获取手部姿势。 例如,返回的姿势可能与从 MRTK3 的手部聚合器子系统中获得的手关节姿势相对应。 此方法将在检索姿势成功时返回结果。 如果数据不可用,此方法的一些实现(如从手关节数据检索姿势)可能会失败。

交互器的 IPoseSource 类型通过 Unity 检查器指定,可以是下列类型之一:

  • FallbackCompositePoseSource。 从有序的姿势源列表中计算合成姿势源。 返回第一个成功返回姿势的姿势源结果。
  • HandBasedPoseSource。 一个抽象类,用于帮助定义基于特定用手习惯的姿势源,并可以访问 MRTK3 的手部聚合器子系统。
  • HandJointPoseSource。 一个姿势源,用于扩展 HandBasedPoseSource 和跟踪特定手上的特定手关节。
  • InputActionPoseSource。 一个姿势源,它从指定的 Unity 输入操作中获取由跟踪位置和旋转组成的姿势。 可以在 MRTK Default Input Actions Unity 资产上找到默认输入操作。
  • PinchPoseSource。 扩展 HandBasedPoseSource 并从 MRTK3 的手部聚合器子系统获取特定手部夹取姿势的姿势源。
  • PolyfillHandRayPoseSource。 扩展 HandBasedPoseSource 并表示手部射线的姿势源。 此手部射线通过从 MRTK3 的手部聚合器子系统中获取手掌和关节位置构建而成。

手部交互器的姿势源类型已在 MRTK3 的默认控制器预制件上指定。 不建议修改预制件的默认姿势源设置。