Skip to main content

SteamVR 骨骼手部追踪驱动指南

本指南旨在帮助自定义 SteamVR 骨骼输入驱动的作者使其驱动与 VRChat 良好配合,因为 VRChat 正在更新至 SteamVR Input 2.0。无论您是希望确保现有驱动正常工作,还是正在创建全新的驱动以通过 SteamVR 与 VRChat 一起使用,都建议查阅 Valve 关于创建 SteamVR 骨骼驱动的文档以及更通用的驱动 API 文档

VRChat 何时以及如何使用 SteamVR 骨骼数据?​

为 Avatar 制作动画​

为了对 Avatar 的手部进行动画,VRChat 将使用绑定到 SkeletonLeftHand 或 SkeletonRightHand 动作的任何骨骼数据,只要它们绑定到输入源并且可用。在所有此类情况下,VRSkeletalMotionRange_WithoutController 的骨骼动画将从动作中获取并存储到中间层。中间层的内部表示将存储骨骼的位置和旋转,但是,在撰写本文档时,应用于 Avatar 的重定向将仅提供旋转。这是因为,除非 Avatar 的 3D 手部网格经过精心设计以适配任意手指长度的关节位置,否则它们可能会出现扭曲或压缩。其缺点是,无法保证任意比例的 Avatar 手部能够准确作为捏合和抓取等操作的视觉反馈。

作为游戏/菜单等输入​

每当骨骼动作的 eSkeletalTrackingLevel 值为 VRSkeletalTrackingLevel.VRSkeletalTracking_Full 时,VRChat 会逐手使用提供的骨骼动画数据作为输入。

回顾一下,eSkeletalTrackingLevel 具有以下值:

  • VRSkeletalTracking_Estimated - 身体部位位置无法由设备直接确定。设备提供的任何骨骼姿势都是通过假设激活按钮、扳机、摇杆或其他输入传感器所需的位置来估计的。Vive 控制器是最著名的例子。
  • VRSkeletalTracking_Partial - 身体部位位置可以直接测量,但自由度低于实际身体部位。某些身体部位位置可能未被设备测量,而是根据其他输入数据估计。例如 Index 控制器、Etee 控制器、仅测量手指弯曲的手套等。
  • VRSkeletalTracking_Full - 身体部位位置可以在整个运动范围内直接测量。例如 Ultraleap 追踪、Meta 手部追踪、MediaPipe 手部追踪、测量每个手指段旋转的手套等。

因此,应该很清楚我们为什么使用这个值——它直接告诉我们所提供的骨骼中包含了多少关于手部姿势的实际信息。它是 API 中唯一提供此类信息的部分,因此我们必须为此目的使用它。

全保真骨骼手部追踪的交互模型建立在我们对骨骼数据进行大量复杂处理的基础之上。我们通过计算从拇指尖到指尖的距离来执行自己的捏合检测,并将其用于 UI 中的"点击",以及围绕拇指与食指、中指、无名指和小指的指尖捏合构建的手势输入。以下是当前的手势动作:

左手:

  • 拇指-食指捏合:
    • 手掌正对用户头部时:打开菜单
    • 手掌背对用户且射线投射激活时:UI 点击
  • 拇指-中指捏合:移动
  • 拇指-无名指捏合并保持(大致面向用户头部时):麦克风静音
  • 拇指-小指捏合并保持(大致面向用户头部时):禁用手势输入

右手:

  • 拇指-食指捏合:无
  • 拇指-中指捏合:转向
  • 拇指-无名指捏合(大致面向用户头部时):跳跃
  • 拇指-小指捏合(大致面向用户头部时):取消/丢弃

这意味着您不需要从与骨骼手部追踪来源的"设备"相关的任何非骨骼、非姿势(布尔、标量、vector2 等)输入组件句柄提供这些动作。实际上,这样做可能会导致双击和/或意外激活。我们将在后面的控制器模拟部分进一步讨论这一点。

这也意味着正确计算关节姿势至关重要。大多数追踪系统都需要某种坐标空间转换来正确匹配 SteamVR 的坐标空间,这个过程可能会引入错误。这引出了下一节——可视化检查您的骨骼数据。

可视化调试骨骼数据​

您可以可视化骨骼数据以进行调试。

准确手部图像

这些在我们的菜单中被称为准确手部(accurate hands)。它们始终出现在菜单和加载场景中,或者当 Avatar 是通用(非人形)Avatar 时。可以在控制菜单中替换Avatar 的手部(仅在本地可见,不通过网络传播)。

如上图标题所示,VRChat 有一个我们称之为准确手部(相对于 Avatar 手部)的手部可视化。这些是我们能提供的最接近底层追踪数据的表示,同时仍然看起来像 3D 手部网格。它们在许多情况下都很有用,但就本文档而言,我们邀请您将其用作调试/确认工具,以确保您提供的追踪数据是准确的,并经过校准以提供最佳的输入体验。进行捏合手势时,指尖应该接触,如果您使用的追踪系统测量每个关节的长度,这也应该反映出来。

要激活此模式,请打开主菜单,然后进入设置页面,再进入控制类别。

控制页面

点击右侧的"Accurate"按钮。手部应该会切换。

控制器模拟​

迄今为止,让光学手指追踪(或较少见的手套)在 SteamVR 中工作的最常见技术是一种称为"控制器模拟"的方法。即自定义 SteamVR 驱动使用输入配置文件、具有 SteamVR 生态系统中现有知名设备(通常是 Touch 或 Index 控制器)属性和渲染模型的追踪设备对象,以冒充它们。然后,将来自源追踪系统(如 Meta Quest 或 Ultraleap)的骨骼数据通过骨骼追踪输入句柄发送,使其进入应用程序的骨骼追踪动作,有时还将保真度级别设置为 Full。虽然这确实将手指追踪数据传入了应用程序,但这并非理想方法。

  • 应用程序无法判断实际使用的硬件是什么
  • 控制器的一些属性(如保真度级别枚举)在骨骼输入组件初始化后无法更改,这使得应用程序无法在运行时响应控制器和手部之间的多模态切换
  • 填充虚拟控制器其他输入(扳机、摇杆、按钮等)的最简单方法是放入简单的不可见手势,从用户体验角度来看这令人沮丧。
  • 其他动作的虚拟输入(前述的扳机、摇杆、按钮等)可能会与从追踪数据生成的应用程序端输入冲突。这可能表现为菜单交互出现双击,或抓取事件同时由驱动和应用程序的手势输入系统触发。这可能导致用户遇到意外和令人困惑的结果。
  • 用户将无法为手指追踪和手持控制器设置独立的绑定。如果用户更改或禁用了手部的输入-动作映射(很可能是为了避免前面列出的问题),该绑定在他们切换回控制器时仍然存在。在点击或抓取的情况下,这将导致他们切换回控制器后无法点击或抓取。

基于这些原因,我们官方不支持这种"控制器模拟"方法。我们有部分代码会尝试检测这种情况,并可能告知用户输入不受支持,这可能会产生意外的副作用。

请不要虚假陈述您发送给 SteamVR/VRChat 的硬件/输入。您可能会注意到 Valve 在他们的文档中指出 VRChat 会检查当前控制器是否为 Knuckles 才激活手指追踪,并且您应该使用他们内置的通用控制器模拟来针对 VRChat。这已不再正确。本指南应被视为权威指南。如果您已经使用此模拟系统为 VRChat 设置了控制器模拟绑定,则应将其移除。

推荐方法​

串流应用​

如果您正在构建将独立设备(如 Meta Quest)连接到 SteamVR 的串流应用,您的应用可能希望除了无控制器手部追踪之外还支持 Touch 等追踪控制器。您可能还希望支持从控制器到手部的快速切换,以及"多模态"使用,即一只手握持控制器,另一只手以无控制器方式追踪。现有的 SteamVR 驱动(如 lighthouse)告诉我们,单个 SteamVR 驱动中可以存在多个输入设备。那么我们推荐的方法是:

为您串流应用支持的每种现有物理控制器类型创建一个虚拟控制器(输入配置文件、追踪设备 ID 和属性、输入组件句柄等)。

创建一个全新的仅用于无控制器手部追踪的虚拟控制器。

物理控制器​

对于这些,只需复制您正在透传的控制器。它们应具有通常应有的相同输入配置文件和组件——摇杆、按钮等,以及相同的追踪设备属性(型号名称、渲染模型、制造商等),相同的输入源/角色(控制器的左手/右手,头显的头部),并将骨骼追踪级别初始化为控制器提供的级别(对于 Touch 这将为 Estimated)。

手部虚拟控制器​

对于这些,您需要一个提供最小必要输入的输入配置文件和组件句柄。这些是:

  • Dashboard
  • System click(用于与仪表板交互)
  • Hand Pose
  • Skeleton

如果您愿意,可以提供其他输入(特别是如果您的手势设计良好和/或有提供反馈的覆盖层),但我们建议将它们从您提供给 VRChat 的默认绑定中移除。

接下来,您需要为虚拟手部控制器提供一组描述性属性。让我们使用一个假设示例:您的串流应用名为"Bitriver",它从 Meta Quest 串流骨骼手部追踪。您可以对每个属性进行如下设置:

左手和右手:

属性值
Prop_ManufacturerName_StringMeta
Prop_TrackingSystemName_StringOculus Insight
Prop_ModelNumber_StringBitriver_Hand
Prop_ControllerType_StringBitriver_Hand
TrackedDeviceClassController
Prop_InputProfilePath_String输入配置文件 json 文件的路径

对于左手:

属性值
Prop_SerialNumber_StringBitRiver_Left_Hand
Prop_ControllerRoleHint_Int321

对于右手:

属性值
Prop_SerialNumber_StringBitRiver_Right_Hand
Prop_ControllerRoleHint_Int322
note

每个支持骨骼输入的控制器都会有一个追踪设备,该追踪设备的 Pose 组件被称为骨骼的 Base Pose——骨骼的变换坐标空间是该 Base Pose 的子级。

当用户的 VR 系统指示正在手部追踪和控制器追踪之间切换时,您需要通过更改在调用 vr::VRserverDriverHost()->TrackedDevicePoseUpdated() 时在每个追踪设备的 Base Pose 组件中发送的布尔值来指示此切换。DriverPose_t 中的相关字段是:

  • poseIsValid
  • deviceIsConnected

当您的控制器处于活动状态时,将它们的 poseIsValid 设置为 true(除非您的控制器 API 为此提供了更好的值,例如您使用的 API 指示某个姿势置信度低而应被拒绝),并将 deviceIsConnected 设置为 true。然后将虚拟手部的 poseIsValid 设置为 false,将虚拟手部的 deviceIsConnected 设置为 false。

当您的手部追踪处于活动状态时,您需要设置相反的值。将控制器追踪设备姿势组件的 poseIsValid 和 deviceIsConnected 设置为 false,当手部被追踪时将虚拟手部的 poseIsValid 设置为 true,未被追踪时设置为 false,并将 deviceIsConnected 设置为 true。

PCVR 外设​

如果您正在为 PCVR 构建单个外设(例如编写自己的计算机视觉手部追踪系统,或构建手套),您只需要执行上一节的第二部分——构建一个适当命名的新虚拟控制器来表示手部。您不需要进行任何控制器与手部的切换。

手指追踪独占模式​

已弃用

VRChat 未来可能会移除此功能。任何不合格的驱动如果为各种动作提供自己的输入,将导致更差的用户体验。

VRChat 在开发期间使用了此功能。所有驱动都为 VRChat 的自定义基于追踪的输入系统可以自行推断的 SteamVR 动作提供了输入。手指追踪独占模式允许在开发期间进行全保真手部追踪,而不会出现双击、双重抓取等问题。

当用户启用手指追踪独占模式时,VRChat 会忽略绑定到除 Pose、SkeletonLeftHand 和 SkeletonRightHand 以外的动作的任何 SteamVR 输入。