手势跟踪

备注

仅在 OpenXR 插件的 1.1.0 及后续版本中可用。

手势跟踪 API 最初是由微软加入 OpenXR 的,目的是让 XR 客户的能够使用用户的手掌和手指的跟踪信息。API 会提供玩家双手所有骨骼的姿势数据,同时也为 XR 运行时环境对此的实现方式预留了一些解释空间。

在 SteamVR 中,对此的支持是基于 Valves 已有的手势跟踪系统添加的,同样会根据控制器的输入推算并提供所有相连的骨骼数据,如果系统没有提供对手势跟踪的原生支持,数据就会从距离传感器推算。

Meta 在为移动端 OpenXR 运行时加入对此 API 的支持时,基于的是他们在 Quest 上已有的手势跟踪功能。请注意,手势跟踪需要在导出设置中启用才能激活。手势跟踪 API 只用于纯手部的跟踪,使用控制器时不会提供姿势数据。

备注

综上所述,使用手势跟踪 API 时应考虑目标平台的能力。已经向 OpenXR 工作组反馈了相关情况,所以日后可能有所改善。

手势跟踪 API 定义了骨骼结构,这是所有 XR 运行时环境都需要遵守的。不过它对于放松姿势下骨骼的朝向以及大小并没有要求。

../../../_images/hand_tracking_bones.png

图片来自 Khronos OpenXR 规范。

手势跟踪 API 与动作系统是相互独立的,不会用到其中的姿势。手势跟踪数据在内部是以全局空间提供的(相对于跟踪体积的原点),因此手势跟踪节点的父节点应该是 ARVROrigin 而不是 ARVRController

插件所暴露的手势跟踪 API 分为两个系统。一个用于更新树中节点的位置,一个用于更新 Skeleton 中的骨骼,让网格能够产生形变。

基于节点的手势跟踪

这个实现的用途是最通用的,因为不需要任何放松姿势下手部模型的信息。请注意,这个插件附带了两个示例场景,分别叫作 left_hand_nodes.tscn(左手节点)和 right_hand_nodes.tscn(右手节点),可以实例化为 ARVROrigin 的子节点。这些场景中包含了自适应的逻辑,会根据提供的骨骼数据大小自动调整所用网格的大小。

../../../_images/arvr_nodes_example.png

这个场景的根是一个 Spatial 节点,附加了 config/OpenXRHand.gdns 脚本。这个类是插件提供的,会将这个空间节点放置到手的基准位置(见上图中的 Palm)并更新其子节点的位置和朝向。

../../../_images/arvr_openxr_hand.png

这里有两个属性:

  • Hand 表示我们跟踪的是左手还是右手的位置。

  • Motion Range 只在 SteamVR 上可用,会限制手的最大闭拢距离,只会在使用由控制器输入推算而来的手势跟踪时用到。

我们的空间节点需要有一些名称写死的子节点,这样我们的手势跟踪系统就能够对它们进行更新。这些节点的类型不重要,我们的示例脚本使用的是 MeshInstance 节点,这样我们就可以实际看到骨骼。

../../../_images/hand_tracking_nodes.png

首先我们找到 Wrist(手腕)子节点,它下面的就是各个手指的节点了。节点的名字都以手指的名称开始,后面跟着的是骨骼的名称。手指的名称有 Thumb(拇指)、Index(食指)、Middle(中指)、Ring(无名指)、Little(小指)。骨骼的名称有 Metacarpal(掌骨)、Proximal(近端)、Intermediate(中部)、Distal(远端)、Tip(尖端)。因此,IndexDistal 指的是食指的远端骨骼。

备注

食指是唯一没有中部骨骼的手指!

这些节点的父子关系很重要,只有严格遵循这个结构,手才会看起来正常。请注意,加入额外的节点不会有问题,示例场景中就加入了一些额外的骨骼网格,让手看起来更完备。另外也请注意,示例场景中为手腕节点添加了脚本,会更新这些额外节点的大小和位置。

基于骨架的手势跟踪

OpenXR 插件支持的第二种方法是将骨骼数据暴露为 Skeleton 节点。此时,解决方案分为两个类,一个用于放置手的位置,一个用于更新之前提到的骨架中的骨骼,从而进行动画。

这种方法可以用来让网格发生形变,看上去会更舒服,但不同平台实现上的区别也会带来一些问题。

备注

微软为 OpenXR 加入了一个 API,可以获取一个正确蒙皮后的手部网格。但因为目前只有他们这个平台支持这个 API,所以本插件还未加入支持。

目前,插件所暴露的是 OpenXR 运行时所提供的数据。插件有一个示例实现,其中的网格是 Valve 提供给公众使用的,不过只有在和 SteamVR 一起使用时才能达到最稳定的效果。这些场景是 scenes/left_hand_mesh.tscn(左手网格)和 scenes/right_hand_mesh.tscn(右手网格),可以作为 ARVROrigin 的子节点使用。

../../../_images/arvr_mesh_example.png

自行实现的大致步骤如下。

备注

实现这种逻辑的最佳方式,是让美术在 3D 软件中使用真实手部尺寸建模并为其创建骨架,骨架必须遵循前文给出的 OpenXR 规范图中的骨骼结构。蒙皮时需要额外注意,如果完整的手势跟踪可用,那么关节之间的距离是由实际玩家的手部尺寸决定的,可能会与 3D 模型中的不同。将模型导入 Godot 后,你就可以加入所需的脚本,让一切正常工作了。

要把这个手部网格放进游戏空间中,你需要为 ARVROrigin 节点添加一个子节点,这个节点需要附加有 config/OpenXRPose.gdns 脚本。导入 3D 文件时,你可以将这个脚本添加到导入后的模型的根节点。

并不是只有手部逻辑才需要使用 OpenXRPose 脚本,它还暴露了动作映射中配置的其他姿势位置。

../../../_images/arvr_openxr_pose.png

这个节点有如下属性可供设置:

  • Invisible If Inactive 在未跟踪手势时自动隐藏该节点。

  • Action 指定跟踪的是动作映射中的哪个动作,需要设为特殊的 SkeletonBase 类型。

  • Path 指定 OpenXR 输入路径,左手为 /user/hand/left 而右手为 /user/hand/right。

下一步是将 config/OpenXRSkeleton.gdns 脚本添加到 3D 模型的骨架节点上。这个脚本也有 OpenXRHand 脚本的两个属性,即 Hand 和 Motion Range,使用方法也一样。

请注意,骨骼的名称已经标准化,下面是骨骼的名称列表,需要根据该骨骼属于左手还是右手加上对应的 _L 或 _R 后缀:

  • Palm

  • Wrist

  • Thumb_Metacarpal

  • Thumb_Proximal

  • Thumb_Distal

  • Thumb_Tip

  • Index_Metacarpal

  • Index_Proximal

  • Index_Intermediate

  • Index_Distal

  • Index_Tip

  • Middle_Metacarpal

  • Middle_Proximal

  • Middle_Intermediate

  • Middle_Distal

  • Middle_Tip

  • Ring_Metacarpal

  • Ring_Proximal

  • Ring_Intermediate

  • Ring_Distal

  • Ring_Tip

  • Little_Metacarpal

  • Little_Proximal

  • Little_Intermediate

  • Little_Distal

  • Little_Tip

最后,跟踪指尖的位置进行物理交互是手势跟踪的常见附加功能,也是标准的 Godot 功能。可以使用 BoneAttachment 节点来实现。将其添加为 Skeleton 节点的子节点,并选择你想要跟踪的骨骼即可。现在你就可以将所需的物理对象添加为这个节点的子项了。