多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Godot 4 OpenXR 手势追踪与控制器追踪统一方案实战:openxr_hand_tracking_demo 项目源码解析

Godot 4 OpenXR 手势追踪与控制器追踪统一方案实战:openxr_hand_tracking_demo 项目源码解析 示例工程【免费下载链接】godot-demo-projectsDemonstration and Template Projects项目地址https://gitcode.com/GitHub_Trending/go/godot-demo-projects点击查看免费下载本篇技术指南以仓库 xr/openxr_hand_tracking_demo 为核心深入讲解在 Godot 4 中如何基于 OpenXR 的手部追踪 APIHand Tracking API、Skeleton Modifier 骨架修改器与 Action Map 动作映射实现光学手势追踪与控制器追踪两种输入方式的统一既能渲染跟随真实手部运动的骨骼手模型又能让玩家抓取并移动场景中的方块。读完本文你将掌握手部追踪数据源的选择逻辑、回退骨架修改器的实现原理、基于交互配置文件的多平台输入绑定以及可复用的拾取Pickup交互范式。项目概览与运行环境本 Demo 展示的是 OpenXR 的手部追踪hand tracking与控制器追踪controller tracking逻辑使用GDScript编写渲染后端为CompatibilityGL Compatibility。从 project.godot 可以看到项目的关键配置[xr] openxr/enabledtrue openxr/reference_space2 openxr/foveation_level2 openxr/foveation_dynamictrue openxr/extensions/hand_trackingtrue openxr/extensions/hand_tracking_unobstructed_data_sourcetrue openxr/extensions/hand_tracking_controller_data_sourcetrue openxr/extensions/hand_interaction_profiletrue shaders/enabledtrue其中reference_space2对应 OpenXR 的local floor参考空间地面为基准的本地空间这是该 Demo 特意采用的一种参考空间后文会单独说明。项目还启用了 Jolt Physics 作为 3D 物理引擎3d/physics_engineJolt Physics因为场景中的可拾取方块是RigidBody3D。运行时玩家会看到一张桌子table.tscn和三个方块box.tscn每只手各挂载一个XRController3D节点tracker 为left_hand/right_hand并通过XRNode3D挂载人类化左手/右手骨骼模型LeftHandHumanoid.gltf/RightHandHumanoid.gltf位于 assets/gltf。核心问题对手部追踪的两种认知流派能不能看到玩家的双手、并让双手与场景元素交互是优秀 XR 体验的关键。本 Demo 要解决的核心矛盾在于业界对手部追踪到底是什么存在两种截然不同的认知且不同 XR 运行时之间能力存在差异。流派一手部追踪 纯光学追踪第一种流派把手部追踪视为一套独立系统专指光学手势追踪。在这种认知下OpenXR 的手部追踪 API 即便宣称支持也只有在使用光学手势追踪时才真正返回数据使用控制器时没有任何手部数据可用开发者必须自行解决显示手部网格 根据控制器输入驱动动画的问题Godot XR Tools 当前版本包含完整的现成方案与此同时Action Map 只适用于控制器输入光学手势追踪激活时开发者需要自己实现手势识别当单个应用需要同时支持控制器追踪和光学手部追踪时这种分而治之的思路会让开发变得相当棘手。流派二统一方案本 Demo 采用第二种流派忽略控制器追踪与光学手部追踪的差异把它们视为同一事物的两种版本。尤其对于 Valve Index 这类控制器、以及各种被当作控制器处理的数据手套来说二者几乎没有可感知的区别手部追踪 API 主要用于可视化玩家的手骨骼位置既可以从控制器输入推断也可以匹配光学追踪结果高级手势识别同样可以使用这套数据且无论数据以何种物理方式获得都能访问Action Map 系统被视为获取输入的首要手段不再局限于控制器输入——XR 运行时负责识别捏合pinch、指点pointing等基础手势并把结果作为可绑定到 Action Map 的输入暴露出来。OpenXR 正在朝这个方向演进本 Demo 也据此构建但并非所有运行时都已跟上。兼容性现状以 README 与项目配置为准SteamVR长期遵循统一方案开箱即用但它把所有设备都当作控制器处理当 Quest 通过 Meta Link / Steam Link 使用光学手部追踪时存在一些短板Meta 原生 Quest 运行时全系 Quest已支持 OpenXR 的 data source extensionGodot 在启用手部追踪时即开启它但 Meta 尚未支持必需的 hand interaction profile extensionMeta Link仍处于滞后状态暂时不支持这套新方案Pico、HTC、Varjo、Magic Leap 等其它运行时对新扩展的支持程度不一。结论与设计取舍由于 XR 运行时的能力差异巨大且目前没有可靠手段检测当前平台的全部能力边界Godot XR Tools 并不依赖手部追踪 API而是采用自研的推断式手部定位方案。但随着越来越多运行时采纳新扩展针对支持这些扩展的平台开发的解决方案已经可以放心地依赖手部追踪 API——本 Demo 展示的正是这种未来形态。手部追踪 API 与 Skeleton Modifier 架构Godot 4.3 的骨架改造手部追踪 API 是本 Demo 可视化玩家双手的核心。在 Godot 4.3 中系统做了重构XR Interface 需要把手部追踪数据转换为 Godot 人形骨架humanoid skeleton的手部骨骼布局。这意味着同一套逻辑在 WebXR、OpenXR 以及任何其它支持该特性的 XR Interface 上都能工作。手部追踪同时利用了 Godot 4.3 新增的Skeleton Modifier逻辑但存在明确的分工骨架的姿态pose在手的局部空间内被驱动骨架的位置由XRNode3D节点提供。这种姿态/位置分离的设计原因是位置总是位于XROrigin3D节点的局部空间内实际项目中经常需要忽略或修改位置信息例如让手吸附到特定物体。从 main.tscn 的场景结构可以清楚看到这一分工LeftHandMesh/RightHandMesh是XRNode3Dtracker 分别为/user/hand_tracker/left与/user/hand_tracker/right负责定位其子节点是导入的LeftHandHumanoid2/RightHandHumanoid2骨架场景骨架节点Skeleton3D下依次挂载了两个 modifierXRHandModifier3D标准的手部追踪骨架修改器从手部追踪器读取骨骼数据XRHandFallbackModifier3D脚本为 xr_hand_fallback_modifier_3d.gd运行时兜底方案。[!NOTE] 手部追踪 API 使用的两个追踪器路径为/user/hand_tracker/left和/user/hand_tracker/right。[!NOTE] Godot 4.4 引入了新的项目设置。若将本工程升级到 Godot 4.4 或更高版本需要打开Project Settings窗口进入OpenXR设置手动启用手部追踪数据源扩展hand tracking source extensions。追踪器选择与姿态回退hand_mesh.gdhand_mesh.gd 是手部网格节点XRNode3D的核心脚本它在每一帧按优先级选择追踪器优先使用手部追踪器若/user/hand_tracker/left或/user/hand_tracker/right存在且has_tracking_data为真就切换到该 tracker并把 pose 重置为default否则回退到控制器追踪器left_hand/right_hand此时优先请求palm_pose若该 pose 不存在或tracking_confidence XR_TRACKING_CONFIDENCE_NONE则改用grippose。与之配套的 hand_controller.gd挂在XRController3D节点上做同样的姿态决策从XRServer.get_tracker(tracker)获取控制器追踪器尝试palm_pose失败则回退grip再写入节点的pose属性。两者共同保证了有光学追踪用光学追踪没有就用控制器推断的平滑切换。调试面板hand_info.gdhand_info.gd挂在 hand_info.tscn 上场景中实例化为LeftHandInfo/RightHandInfo把运行时状态实时渲染成屏幕文字可用于验证数据来源当前交互配置文件interaction profile会去掉/interaction_profiles/前缀展示正在使用palm_pose还是grip_pose以及追踪置信度HIGH/LOW/NONE手部追踪器的数据源类型HAND_TRACKING_SOURCE_UNOBSTRUCTED即光学手部追踪、HAND_TRACKING_SOURCE_CONTROLLER即由控制器推断、HAND_TRACKING_SOURCE_NOT_TRACKED表示无数据源等当手部追踪无数据时显示控制器上的回退小球网格fallback_mesh场景中是一个小SphereMesh。半身体追踪 API顺带一提Godot 现在也支持包含手部追踪在内的半身/全身追踪half/full body tracking但这不属于本 Demo 的范畴且该功能仅在一小部分 XR 运行时上可用。回退骨架修改器XRHandFallbackModifier3D当 XR 运行时不支持手部追踪、或不支持控制器数据源时xr_hand_fallback_modifier_3d.gdclass_name XRHandFallbackModifier3D extends SkeletonModifier3D负责兜底用 Action Map 中的 trigger 和 grip 输入分别驱动食指与其余三根手指的弯曲同时手的位置基于通过 Action Map 访问的 pose 来确定。其核心流程_process_modification()通过get_skeleton()拿到骨架向上遍历找到父级XRNode3D只有当父节点的 tracker 是left_hand/right_hand时才继续表示当前没有独立的手部追踪器从XRServer.get_tracker(xr_parent.tracker)读取trigger与grip两个输入值可通过export var trigger_action/grip_action自定义动作名在 main.tscn 中左右手的grip_action都被覆写为pickup遍历全部骨骼按骨骼名硬编码动画LeftHand/RightHand整体偏移手掌中心Vector3(-0.015, 0.0, 0.04)/Vector3(0.015, 0.0, 0.04)这一偏移要求使用 palm pose食指LeftIndexDistal/Intermediate/Proximal等沿 X 轴旋转deg_to_rad(45.0) * trigger远端/中节与deg_to_rad(20.0) * trigger近节中指、无名指、小指的各节骨骼沿 X 轴旋转deg_to_rad(90.0) * grip通过skeleton.set_bone_pose(i, t)写回每一根骨骼的姿态。由于每帧都会对所有骨骼重设姿态代码注释特别提醒position all bones in case we need to reset some为需要复位时重置所有骨骼。关于手部定位默认使用palm pose——它在 OpenXR Action Map 中有明确定义。由于 palm pose 是可选能力必要时会回退到grip posegrip pose 的定位与 palm pose 接近但其朝向在不同 XR 运行时之间存在差异可能导致手部网格错位。在hand_controller.gd与hand_mesh.gd中都能看到这套palm → grip的回退逻辑。Action Map为手部追踪定制的输入方案本 Demo不使用默认 Action Map而是创建了针对本用例定制的动作映射openxr_action_map.tres。所有动作都定义在godot动作集action set中且左右手的顶层路径均为/user/hand/left与/user/hand/right。动作列表动作类型用途palm_poseposeaction_type 3默认用于定位 XRController3D 节点和手部回退逻辑grip_poseposeaction_type 3当 palm pose 不被支持时使用pickup布尔/浮点输入作为拾取物体的输入并做相应绑定trigger浮点输入仅用于动画驱动食指弯曲haptic振动输出action_type 4目前 Demo 未使用仅为将来预留[!NOTE] 该 Action Map 使用的追踪器是left_hand和right_hand。交互配置文件绑定openxr_action_map.tres为不同厂商的交互配置文件interaction profile声明了具体绑定展示了一整套跨平台兼容的输入映射策略交互配置文件关键绑定/interaction_profiles/khr/simple_controllerselect/click→pickupgrip_surface/pose→palm_posegrip/pose→grip_pose/interaction_profiles/ext/hand_interaction_extgrasp_ext/value→pickupgrip_surface/pose→palm_posegrip/pose→grip_pose/interaction_profiles/oculus/touch_controllersqueeze/value→pickuptrigger/value→triggergrip_surface/pose→palm_pose/interaction_profiles/valve/index_controllersqueeze/value→pickuptrigger/value→triggergrip_surface/pose→palm_pose/interaction_profiles/microsoft/hand_interactionsqueeze/value→pickupgrip_surface/pose→palm_pose/interaction_profiles/bytedance/pico4_controllersqueeze/value→pickuptrigger/value→triggergrip_surface/pose→palm_pose其中grip_surface/pose握持表面 pose普遍绑定到palm_posegrip/pose绑定到grip_pose这正是上文优先 palm pose、回退 grip pose策略的配置基础。输入特性与手势交互当光学手部追踪激活时本 Demo 依赖 OpenXR 的hand interaction profile 扩展没有该扩展支持Demo 无法完整运行。README 中明确解释了为何不自行实现手势识别兜底可以通过检测XRController3D节点是否绑定交互配置文件、再做自己的手势检测来绕过但这会大幅增加 Demo 复杂度且预期该扩展很快会被广泛采用故未包含。[!NOTE] 部分头显在启用手部追踪时会支持 simple controller简单控制器。simple controller 交互配置文件不支持抓握输入。此时你可以用捏合手势抓取方块——即用拇指指尖触碰食指指尖。MSFT 与 HTC 手势交互扩展MSFT Hand interaction 扩展微软引入的手势交互扩展Godot 现已支持且本工程已配置启用可见于microsoft/hand_interaction交互配置文件与hand_interaction_profile项目设置。Meta 等多家厂商也加入了对该扩展的支持。该扩展同时支持抓握grab手势与捏合pinch手势因此你可以**握拳grab motion**来抓起本工程中的方块。HTC Hand interaction 扩展HTC 引入的手势交互扩展Godot 已支持但本 Demo 尚未实现。该扩展引入了两个新的追踪器使用它需要修改XRController3D节点上的 tracker 才能生效。拾取交互PickupHandler3D 与 PickupAbleBody3D拾取逻辑拆分为两个可复用组件pickup_handler.gd / pickup_handler.tscn一个Area3D节点作为XRController3D的子节点负责检测范围内可拾取物体、选出最近的一个并处理该手的拾取逻辑在 main.tscn 中左右手的PickupHandler均实例化此场景局部偏移 ±0.05pickup_able_body.gd可添加到RigidBody3D的脚本让物体可被拾起/放下box.tscn 即使用该脚本。拾取端PickupHandler3D核心要点export var detect_range : float 0.3检测半径默认 0.3 米通过修改CollisionShape3D内SphereShape3D的radius生效每物理帧_physics_process调用_update_closest_body()遍历get_overlapping_bodies()只考虑PickupAbleBody3D且未被拾起的物体用平方距离选出最近者并通知旧/新候选体更新高亮add_is_closest/remove_is_closest拾取判定采用自定义迟滞阈值pickup_value (was_pickup_pressed ? 0.4 : 0.6)。注释说明了原因——OpenXR 虽能返回布尔值但各平台对阈值的处理差异很大故自行实现按下阈值 0.6保持按下只需 0.4防抖松开逻辑已拾起且pickup_pressed变为假时调用picked_up_body.let_go()pickup_action默认为pickup与 Action Map 中的动作对应。物体端PickupAbleBody3Dpick_up()记录original_parent与当前全局变换 → 从原父节点移除 → 挂到拾取者picked_up_by.add_child(self)→freeze true冻结刚体 → 用 Tween 把物体吸附到目标变换snap_to处留有待补充的吸附位姿代码let_go()从拾取者移除 → 挂回original_parent→ 恢复全局变换 →freeze false重新激活物理_update_highlight()当物体未被拾起但处于某个拾取范围的最近候选时给所有MeshInstance3D子节点套上高亮材质material_overlay材质预载自 shaders/highlight_material.tres否则移除——玩家据此知道哪些方块当前可被抓起。本地地面参考空间与重新居中提示本 Demo 的最后一个显著特点是使用local floor 参考空间即 project.godot 中的openxr/reference_space2。在该参考空间下当用户触发重新居中recenter逻辑时XR 运行时会以XROrigin3D节点为基准重新居中玩家。不同运行时在启动时的表现并不一致Quest 会尝试记住上次重新居中的位置而 SteamVR 倾向于重置为默认值。因此无法保证 Demo 启动时玩家正好处于正确位置——这就是场景中RecenterInfo一个Label3D显示如果没有面向桌子请重新居中头显提示的原因。提示内容针对平台给出具体操作Quest按住右手控制器的 Meta 键数秒SteamVR打开菜单选择 recenter 并按其指引操作。此外start_vr.gd 中的_on_openxr_pose_recentered会响应 OpenXR 的pose_recentered信号并重新广播供游戏实现自行处理居中后的逻辑。OpenXR 会话启动与运行状态管理start_vr.gdmain.tscn 中的StartVR节点脚本 start_vr.gd负责 OpenXR 会话的启动与生命周期管理_ready()通过XRServer.find_interface(OpenXR)获取接口初始化成功后开启vp.use_xr true并关闭 V-Sync由 OpenXR 负责同步同时连接session_begun、session_visible、session_focussed、session_stopping、pose_recentered五个会话事件若 OpenXR 未能实例化则打印错误并退出_on_openxr_session_begun()读取当前刷新率在可用刷新率中挑选不超过maximum_refresh_rate默认 90的最高值并应用随后把物理帧率同步为刷新率Engine.physics_ticks_per_second roundi(current_refresh_rate)注释说明这是为了避免抖动当前未使用物理插值焦点管理当会话从聚焦转为可见玩家摘下头显时暂停游戏process_mode PROCESS_MODE_DISABLED重新聚焦时恢复另外start_vr.gd会尝试开启可变速率着色VRS若不可用且 Foveation 级别为 0则提示在项目设置中将 Foveation 级别设为 High。运行指南在 PCVR 上运行可像普通 PCVR 项目一样运行前提是已安装 OpenXR 运行时。本工程已在Oculus 客户端与SteamVR OpenXR 运行时下测试通过。注意Godot 目前无法使用 WMR OpenXR 运行时若使用 WMR请安装带 WMR 支持的 SteamVR。在独立 VR 设备上运行必须先安装Android 构建模板和OpenXR 加载器插件OpenXR loader plugin并为设备配置导出模板然后按照 Godot 官方手册中部署到 Android的步骤完成导出与安装。project.godot中textures/vram_compression/import_etc2_astctrue即为移动端ETC2/ASTC纹理压缩的导出配置renderer/rendering_method.mobilegl_compatibility表明移动端同样使用 Compatibility 渲染。升级到 Godot 4.4 的注意事项升级后需在 Project Settings → OpenXR 中手动启用手部追踪数据源扩展unobstructed data source 与 controller data source否则手部追踪扩展将不会生效。小结本 Demo 的价值在于提供了一套面向 OpenXR 手部追踪时代的完整参考实现XRHandModifier3DXRNode3D的骨架局部姿态 / 场景内定位分离架构、hand_mesh.gd/hand_controller.gd的光学追踪优先与 palm→grip 姿态回退、XRHandFallbackModifier3D的控制器输入兜底、覆盖六类交互配置文件的多平台 Action Map以及PickupHandler3D/PickupAbleBody3D组成的拾取交互范式。对于想在 Godot 中构建同时支持光学手势追踪与控制器追踪、并让双手与场景互动的 XR 应用的开发者这份工程既是可直接运行的样板也是理解 Godot 4.3 XR 架构演进的最佳切入点。赞分享示例工程【免费下载链接】godot-demo-projectsDemonstration and Template Projects项目地址https://gitcode.com/GitHub_Trending/go/godot-demo-projects点击查看免费下载相关推荐Dify.AI追踪系统请求追踪方案Dify.AI追踪系统请求追踪方案 概述 在现代AI应用开发中请求追踪Request Tracing是确保应用可观测性的关键技术。Dify.AI作为领先人工智能大模型LLMOpsAI 应用RAGAI Agent低代码上一篇Forem /dev_tools 开发者仪表盘本地会话伪装与开发工具集实战指南下一篇Enquirer安全最佳实践防止命令注入与恶意输入创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表