mcpField
H
hz-unity-face-tracking

hz-unity-face-tracking

github.com/meta-quest开发241146 次安装5 周前更新
代码仓库

关于 hz-unity-face-tracking

此技能提供在 Unity 中将标准 52 个 ARKit blendshape 连接到 Meta VR 面部追踪的完整流程,面向使用 ARKit 绑定模型的开发者。它使用公开的 OVRCustomFace 扩展点,并另外说明如何通过 OVREyeGaze 驱动眼神。

主要功能

  1. 01扫描 blendshape 名称并生成映射
  2. 02通过 OVRCustomFace 驱动面部表情
  3. 03配置 Meta XR 面部追踪与所需权限
  4. 04用 OVREyeGaze 驱动眼神 blendshape 或眼骨
  5. 05提供场景设置、验证和故障排查步骤

适用场景

  1. 01让 ARKit 绑定的头部模型响应 Quest 面部追踪
  2. 02在说话时通过音频驱动口部表情
  3. 03为没有眼骨的模型连接眼神 blendshape
  4. 04排查 blendshape 未匹配或追踪无响应

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/meta-quest/agentic-tools --skill hz-unity-face-tracking

安装后在 Claude Code 中输入/hz-unity-face-tracking确认已加载。

附带文件references/ARKitEyeGazeBlendshapeDriver.csreferences/ARKitOVRCustomFace.cs

详细介绍

适用于 ARKit Rig 模型的 Unity 面部追踪(Meta Movement SDK)

端到端操作指南:让使用标准 52 个 ARKit 混合形状绑定的头部/面部模型,根据 Meta VR 用户的面部动作进行动画驱动。使用公开的 OVRCustomFace 扩展钩子——无需 OVR_INTERNAL_CODE,可发布到第三方平台。

模型的混合形状名称必须遵循 ARKit 命名约定(驼峰命名,使用 _L/_R 后缀,例如 eyeBlink_L、jawOpen、mouthSmile_R)。系统会自动去除 blendShape2.eyeBlink_L 这样的可选前缀。

适用场景

  • 用户有一个包含 ARKit 命名混合形状的 FBX/GLB/网格,并希望通过 Meta VR 面部追踪驱动它。
  • 用户提出:“用我的面部动作驱动这个头部模型”“通过面部追踪驱动这些混合形状”“用 Movement SDK A2E 驱动我的模型”“将 ARKit 形状接入 Meta VR”。
  • 目标设备:Quest Pro、Quest 3、Quest 3S(Quest 2 不执行任何操作——没有面部摄像头)。

先决条件检查清单

  1. 支持面部追踪的 Quest 头显(Pro/3/3S)。
  2. Packages/manifest.json 中包含以下软件包:
    • com.meta.xr.sdk.core(Meta XR Core——提供 OVRFaceExpressions、OVRCustomFace)
    • com.meta.xr.sdk.movement(Meta Movement SDK——A2E 和重定向辅助工具)
  3. Assets/Oculus/OculusProjectConfig.asset(通过“Project Settings”→“Meta XR”验证):
    • faceTrackingSupport: 1(Supported)或 2(Required)
    • 如果绑定包含注视方向,则设置 eyeTrackingSupport: 1
  4. Android 清单(Assets/Plugins/Android/AndroidManifest.xml)权限:
    • <uses-feature android:name="oculus.software.face_tracking" android:required="false" />
    • <uses-permission android:name="com.oculus.permission.FACE_TRACKING" />
    • <uses-permission android:name="android.permission.RECORD_AUDIO" />(A2E 必需)
    • 注视追踪需要:oculus.software.eye_tracking 和 com.oculus.permission.EYE_TRACKING
  5. 场景中包含 OVRCameraRig,且其 OVRManager.FaceTrackingDataSources 包含 Audio(A2E)——如果跳过 Audio,嘴部动作就只能通过视觉方式驱动。

每次更改 OculusProjectConfig 后,都要重新生成清单(见下文的重新生成清单)。

实现方法(概述)

  1. 将 ARKitOVRCustomFace 脚本(位于 references/ARKitOVRCustomFace.cs)放入项目。
  2. 在 OVRCameraRig 上(或场景中的任意位置)添加 OVRFaceExpressions 组件。
  3. 在包含模型 SkinnedMeshRenderer 的 GameObject 上添加 ARKitOVRCustomFace。添加组件会触发 Reset(),该方法会扫描网格的混合形状名称,自动填充混合形状→FaceExpression 映射。
  4. 将组件的 FaceExpressions 字段关联到 OVRFaceExpressions 实例。
  5. 如需注视追踪:添加 ARKitEyeGazeBlendshapeDriver(见下文眼动追踪(仅使用面部表情无法实现))。
  6. 使用 Meta XR Link 按下 Play,或构建 APK 并侧载安装。首次启动时,请接受面部追踪/眼动追踪/麦克风权限提示。

完整流程就是这些。详情见下文。

眼动追踪(务必执行——仅靠面部表情无法实现)

不要依赖 OVRFaceExpressions.EyesLook* 来获取眼动方向。 这些字段是根据面部摄像头画面推导出来的,而不是来自专用眼动追踪器。在 Quest Pro 上,即使其他面部追踪功能正常工作,这些字段也经常为零或含有噪声。正确的 API 是 OVREyeGaze,它直接使用眼动追踪器。

对于带有 eyeLook* blendshape(没有眼骨骼)的 ARKit 骨骼,本技能提供了 ARKitEyeGazeBlendshapeDriver:

  • 从两个 OVREyeGaze 组件读取视线旋转(每只眼睛一个,TrackingMode = HeadSpace)。
  • 计算相对于头部参考对象(例如 CenterEyeAnchor)的旋转。
  • 将俯仰角分解为 → eyeLookUp/Down_{L,R},将偏航角分解为 → eyeLookIn/Out_{L,R}(遵循 ARKit 的“_In = 朝向鼻子”约定)。
  • 在 LateUpdate 中写入权重,因此会覆盖 ARKitOVRCustomFace 在 Update 中写入的值。

对于带有眼骨骼(没有 eyeLook* blendshape)的骨骼,跳过 blendshape 分解,直接将 OVREyeGaze 分别设为每个眼骨骼的父对象,并将 ApplyRotation = true——该组件会直接旋转骨骼。

眼动设置(blendshape 骨骼)
  1. 在 CenterEyeAnchor(或头部变换)下创建两个空 GameObject:LeftEyeGaze、RightEyeGaze。
  2. 分别添加 OVREyeGaze。设置 Eye = Left / Right、TrackingMode = HeadSpace、ApplyPosition = false、ApplyRotation = true、ConfidenceThreshold = 0.5。
  3. 将 ARKitEyeGazeBlendshapeDriver 添加到头部的 SkinnedMeshRenderer GameObject(与 ARKitOVRCustomFace 一起)。连接 leftEye、rightEye 和 referenceFrame(即 CenterEyeAnchor)。
  4. 根据需要调整 maxAngleDeg(默认 30°)和 smoothing(默认 0.4)。

分步说明

1. 安装脚本

将 references/ARKitOVRCustomFace.cs 复制到 Assets/Scripts/ARKitOVRCustomFace.cs。它定义了公开且可随第三方产品发布的 ARKit ↔ OVR FaceExpression 对照表,以及 MapBlendshapes() 方法。该方法会扫描 SkinnedMeshRenderer.sharedMesh.GetBlendShapeName(i),去掉最后一个 . 之前的所有前缀,将名称转为小写,并与对照表匹配。未匹配的网格 blendshape 会被设为 OVRFaceExpressions.FaceExpression.Max(哨兵值——运行时会跳过)。

2. 启用 Movement SDK 和权限

项目设置:

Project Settings → Meta XR → Face Tracking Support = Supported
Project Settings → Meta XR → Eye Tracking Support = Supported (if needed)

然后重新生成清单文件。

重新生成清单文件

在运行中的 Editor 中,通过 run_script 调用 SDK 的生成器——该类型可直接解析,因此 无需反射:

// AgentScripts/UpdateManifest.cs
public static class UpdateManifest
{
    public static string Run()
    {
        OVRManifestPreprocessor.GenerateOrUpdateAndroidManifest(true);   // silentMode: true
        return "ok";
    }
}
unity command run_script --file AgentScripts/UpdateManifest.cs --entry UpdateManifest.Run --format json

必须设置 silentMode: true——否则生成器会打开一个模态对话框,阻塞请求。然后确认上方清单中的面部追踪条目确实已写入 Assets/Plugins/Android/AndroidManifest.xml。完整的清单工作流以及禁止手动编辑受管条目的规则,请参阅 hz-unity-meta-core-sdk。

替代方法:使用 Editor 菜单 Meta → Tools → Update AndroidManifest.xml;或者,如果你使用的是带 Meta Unity 扩展的 Unity MCP 服务,也可以使用 meta_update_android_manifest 工具。

3. 场景设置
Scene Hierarchy
├── OVRCameraRig                         (OVRQuickActionsAPI.AddOVRInteractionRig(), or the
│   │                                     OVRCameraRig prefab; meta_add_camerarig under MCP)
│   └── (add) OVRFaceExpressions         component
└── YourHeadModel
    └── ...SkinnedMeshRenderer GO...
        ├── SkinnedMeshRenderer          (existing)
        └── (add) ARKitOVRCustomFace     component
             └── FaceExpressions = the OVRFaceExpressions ref
             └── retargetingType = Custom (set automatically by base when overriding)
             └── Mappings[] = auto-filled on Reset()
             └── BlendShapeStrengthMultiplier = 100 (default; OVR weights are 0–1, mesh wants 0–100)

如果头部有多个 SkinnedMeshRenderer(例如牙齿和舌头使用了独立网格),请将 ARKitOVRCustomFace 添加到每个网格上。

4. OVRManager——将 Audio 启用为数据源(A2E)

在 OVRCameraRig 的 OVRManager 组件上:

  • Face Tracking Data Sources → 同时勾选 Visual 和 Audio(Audio = A2E;当视觉面部摄像头无法看到某些内容时,例如说话或受到遮挡,会根据麦克风输入生成嘴部形状)。
5. 如果组件已存在,则触发映射

添加组件时(Reset())以及 Mappings 为空时的 OnValidate() 中,都会自动运行 MapBlendshapes()。如果需要手动重新映射(例如重新导入 FBX 后),请使用组件检查器的上下文菜单 → Map Blendshapes,或通过脚本调用:

go.GetComponent<ARKitOVRCustomFace>().MapBlendshapes();

通过 run_script 在运行中的 Editor 中操作时,可以直接引用项目脚本的类型(它位于 Assets/ 中),因此只需调用它:

// AgentScripts/Remap.cs
using UnityEngine;

public static class Remap
{
    public static string Run(string modelName)
    {
        var smr = GameObject.Find(modelName).GetComponentInChildren<SkinnedMeshRenderer>();
        smr.gameObject.GetComponent<ARKitOVRCustomFace>().MapBlendshapes();
        return $"Remapped {smr.sharedMesh.blendShapeCount} blendshapes on {smr.name}";
    }
}
unity command run_script --file AgentScripts/Remap.cs --entry Remap.Run --args '["YourHeadModel"]' --format json
unity command save_scene --format json

在 Unity MCP 服务下,动态程序集无法访问 OVR/MSDK 类型,因此可使用无需反射的替代方法 SendMessage:

GameObject.Find("YourHeadModel")
    .GetComponentInChildren<SkinnedMeshRenderer>().gameObject
    .SendMessage("MapBlendshapes", SendMessageOptions.RequireReceiver);
6. 验证
  • 在 Editor 中,使用 Meta XR Link / Quest Link 进入 Play 模式并做出表情。模型应当同步显示这些表情。通过 CLI 驱动这一流程:

    unity command clear_console --format json
    unity command editor_play --format json          # blocks until play mode is live
    # --- the wearer now makes faces; ask the user to confirm before continuing ---
    unity command get_console_logs --severity error --format json
    unity command editor_stop --format json
    

    不要连续执行这四项操作。 在 editor_play 后立即执行 editor_stop,会导致没有时间让任何人做出表情,因此无法验证任何内容。进入 Play 模式后,请让用户做出表情并报告所见,然后再读取日志并停止。

  • 在设备上,构建 APK,侧载(metavr app install <apk>),首次启动时授予 Face Tracking 和 Microphone 权限,并在做出表情时运行 metavr adb logcat --follow --tag Unity,以发现映射错误。

  • 在编辑时检查映射:检查 ARKitOVRCustomFace 组件。Mappings.Length 应等于 SkinnedMeshRenderer.sharedMesh.blendShapeCount。MapBlendshapes() 的 Console 日志会报告 mapped X/N blendshapes——X 应为 50(如果 FBX 包含全部形状,则为 52)。无需打开 Inspector 即可读取序列化数组长度——--field 接受的是 SerializedProperty 路径,因此需要使用 OVRCustomFace 上的 [SerializeField] 后备字段 _mappings,而不是未序列化的公共属性 Mappings:

    unity command get_serialized_fields --target <head GO> --component ARKitOVRCustomFace --field _mappings --format json
    

故障排除

症状原因 / 解决方法
Console 中显示 mapped 0/N网格的 blendshape 名称不符合 ARKit 规范。使用 mesh.GetBlendShapeName(i) 验证。名称必须符合 eyeBlink_L、jawOpen 等格式——不区分大小写,并且会去除最后一个 . 之前的前缀。
面部表情僵住未分配 OVRFaceExpressions,或场景中没有启用面部追踪的 OVRCameraRig/OVRManager。运行时检查 OVRFaceExpressions.FaceTrackingEnabled 和 ValidExpressions。
说话时嘴巴不动A2E 已禁用。在 OVRManager → Face Tracking Data Sources 下启用 Audio,并确保设备已授予 RECORD_AUDIO 权限。
眼睛不眨网格的眼睑形状未命名为 eyeBlink_L/_R。请重命名,或向 ARKitTable 添加自定义行。
眼睛不动(无法环顾四周)你依赖的是 OVRFaceExpressions.EyesLook*,而非 OVREyeGaze——请切换到 ARKitEyeGazeBlendshapeDriver。
眼球注视方向错误请注意 ARKit ↔ OVR 之间的有意交换:ARKit 的 eyeLookIn_L(眼睛朝鼻侧看,即向右)映射到 OVR 的 EyesLookRightL。表中已经如此设置——不要“修正”它。
权重看起来只有一半强度或被钳制BlendShapeStrengthMultiplier 默认值为 100,因为 Unity blendshape 的范围是 0–100,而 OVR 的范围是 0–1。除非有意为之,否则不要调低此值。
编译错误 cannot change access modifiers when overriding基类方法在另一个程序集中为 protected internal。重写时必须使用 protected(而非 protected internal)——提供的脚本中已正确设置。
启动时断言 _mappings out of sync with shared mesh生成映射后网格发生了变化。通过组件上下文菜单重新运行 MapBlendshapes()。

ARKit ↔ OVR FaceExpression 映射(参考)

52 个 ARKit 形状与基于 FACS 的 OVR 枚举并非一一对应。ARKitTable 中包含以下值得注意的选择:

  • 名称居中的 ARKit 形状对应一对左右 OVR 表情(browInnerUp、cheekPuff、mouthFunnel、mouthPucker、mouthRollLower、mouthRollUpper),此时仅选择 左侧 OVR 表情。如果用户使用这些表情时,模型的嘴部或眉毛明显不对称,而你希望实现真正对称的驱动,可创建子类,并在自定义 MapBlendshapes 中将左右两侧相加(采用类似 SlothARKitFaceDriver 的逐个混合形状求和方式)。对于大多数头部模型,选择左侧即可,因为面部大致对称,且不对称程度低于视觉阈值。
  • eyeLookIn/Out 按侧别相对于 OVR 左右语义进行了特意调换(参见故障排除)。
  • mouthClose → LipsToward(最接近的 FACS 对应项)。
  • tongueOut → TongueOut(需要 HorizonOS ≥ 65 和舌头追踪;否则值保持为 0)。

此技能中的文件

  • SKILL.md — 本文件
  • references/ARKitOVRCustomFace.cs — 可直接使用的 OVRCustomFace 子类
  • references/ARKitEyeGazeBlendshapeDriver.cs — OVREyeGaze → eyeLook* 混合形状驱动器(LateUpdate)

为何采用此方法(与其他方案相比)

  • 自定义 MonoBehaviour 驱动器,读取 OVRFaceExpressions[expr] 并直接写入混合形状权重:可行,但无法集成 MSDK 的校正器、眼部约束或未来的重定向升级。只有在无法扩展 OVRCustomFace 时才使用。
  • OVRCustomFace + RetargetingType.ARKitBlendshapes:简洁,但在公开的 Oculus Integration 包中受 #if OVR_INTERNAL_CODE 保护——无法作为 3P 产品发布。
  • OVRCustomFace + RetargetingType.Custom + GetCustomBlendShapeNameAndExpressionPairs 重写(本技能):完全使用公开 API,可发布,并且能获得基类的所有行为(数据有效性门控、权重缩放、网格断言)。

评论

支持 Markdown