> For the complete documentation index, see [llms.txt](https://docs.convai.com/api-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/web-plugins/convai-web-sdk/lipsync-and-blendshape/metahuman-limit-drivers-required-for-mha.md).

# 应用 MetaHuman 限制驱动器

将 MetaHuman 绑定耦合、调校后的增益和淡入淡出包络应用到 251 通道流中，使角色的嘴唇保持闭合，同时下颌移动。

将 MetaHuman 绑定耦合应用到每个 `mha` 帧，在将其写入角色的 morph targets 之前。当前面由以下内容驱动的角色时，请使用此页： `mha` 格式在每次下颌运动时都会露出牙齿。最终，嘴唇在下颌张开时仍保持接触，嘴部看起来像在说话，而不是在开合摆动。

### 为什么 mha 流需要耦合

这里使用的 `mha` 该流是基于 Unreal 的 MetaHuman 绑定制作的，它会用一些控制项驱动其他控制项。同一角色的 glTF 导出包含 morph targets，但不包含这些耦合。最明显的是 `jawOpen` 驱动这四个 `mouthLipsTogether*` 控制项：没有它，每次下颌张开都会使嘴唇分开并露出牙齿。

每个耦合都会将目标通道设为其自身值与源值中的较大者。SDK 会应用这 11 个耦合：

| 源               | 目标                       |
| --------------- | ------------------------ |
| `eyeBlinkL`     | `eyeLidPressL`           |
| `eyeBlinkR`     | `eyeLidPressR`           |
| `noseWrinkleL`  | `noseWrinkleUpperL`      |
| `noseWrinkleR`  | `noseWrinkleUpperR`      |
| `mouthStretchL` | `mouthStretchLipsCloseL` |
| `mouthStretchR` | `mouthStretchLipsCloseR` |
| `jawOpen`       | `jawOpenExtreme`         |
| `jawOpen`       | `mouthLipsTogetherUL`    |
| `jawOpen`       | `mouthLipsTogetherUR`    |
| `jawOpen`       | `mouthLipsTogetherDL`    |
| `jawOpen`       | `mouthLipsTogetherDR`    |

这些名称是以下内容的缩写形式： `CTRL_expressions_*` 通道。 `MHA_LIMIT_DRIVERS` 会导出同一张表，但已解析为帧索引，作为以下项的条目： `{ source, target, sourceIdx, targetIdx }`.

### 先决条件

* 唇同步已启用，使用 `blendshapeConfig.format` 设置为 `'mha'`，并且帧读取自 `client.blendshapeQueue`时发送。参见 [使用 blendshape 驱动面部动画](/api-docs/zh/cha-jian-yu-ji-cheng/web-plugins/convai-web-sdk/lipsync-and-blendshape.md).
* 其 morph targets 以以下内容命名的角色： `CTRL_expressions_*` 通道。参见 [Blendshape 通道顺序参考](/api-docs/zh/cha-jian-yu-ji-cheng/web-plugins/convai-web-sdk/vanilla-typescript/mappings-reference.md).

### 将耦合应用到一帧

当你已经自行将帧写入 morphs 时，请先调用 `applyMhaLimits` 并先对每一帧调用它。它会原地修改帧，对少于 251 个值的帧不执行任何操作。

流会发送 `noseWrinkleUpperL` 并且 `noseWrinkleUpperR` 在回复的每一帧上都恰好为 1.0，这会被读作僵硬的冷笑。 `applyMhaLimits` 而唇同步处理器会将这两个通道上 0.999 或更高的值视为未设置，因此耦合会从以下内容推导它们： `noseWrinkle`。如果你不使用这些辅助工具而是直接将原始帧写入 morphs，请自行清除这两个通道。

```ts
import { applyMhaLimits } from '@convai/web-sdk/lipsync-helpers';

function applyMhaFrame(frame: Float32Array) {
  const shaped = new Float32Array(frame);
  applyMhaLimits(shaped);
  applyFrame(shaped);
}
```

在修改帧之前先复制它，如上所示，以便队列中保存的值保持不变。

### 使用唇同步处理器

`createLipsyncProcessor('mha')` 它封装了整个逐帧流水线：先做嘴部对称处理，然后应用耦合，再应用每通道增益，接着是淡入淡出包络，最后写入 morph targets。

```ts
import { createLipsyncProcessor } from '@convai/web-sdk/lipsync-helpers';

const lipsync = createLipsyncProcessor('mha', { skipEyeChannels: true });
const bindings = lipsync.bindAll(gltf.scene);

// 每个渲染 tick：传入要播放的帧；角色静音时传入 null。
lipsync.tick(frameOrNull, deltaSeconds, bindings);
```

处理器返回以下成员：

| 成员                                    | 作用                                                                   |
| ------------------------------------- | -------------------------------------------------------------------- |
| `process(frame, deltaSeconds)`        | 对一帧进行整形，并返回本次 tick 要写入的值，或者 `null` 在静音后包络稳定下来后返回。返回的数组会被复用；如需保留，请复制。 |
| `bindMesh(mesh)`                      | 解析一个网格的 morph 槽并返回一个绑定，或者 `null` 当没有通道匹配时。                           |
| `bindAll(root)`                       | 遍历场景，并绑定每个具有匹配 morph targets 的网格。                                    |
| `apply(output, bindings)`             | 将处理后的帧写入绑定。对以下情况不执行任何操作： `null`.                                     |
| `tick(frame, deltaSeconds, bindings)` | `process` 并且 `apply` 在一次调用中。                                         |
| `profile`                             | 当前使用的格式配置文件。                                                         |

`bindMesh` 并且 `bindAll` 还会原地修复一个已知的导出问题：某个名称在前缀后缺少下划线，例如 `CTRL_expressionsjawOpen`会被重命名为 `CTRL_expressions_jawOpen` 在网格的 `morphTargetDictionary`.

各选项及其默认值：

| 选项                    | 默认值                               | 效果                                  |
| --------------------- | --------------------------------- | ----------------------------------- |
| `symmetrizeMouth`     | `true`                            | 对每一对左右嘴部和下颌进行平均。                    |
| `applyLimits`         | `true`                            | 应用上述 11 个耦合。                        |
| `useRecommendedGains` | `true`                            | 从下面的推荐增益表开始。                        |
| `gainOverrides`       | `{}`                              | 按短名称指定的每通道增益，例如 `{ jawOpen: 0.8 }`. |
| `skipEyeChannels`     | `false`                           | 保持眼部通道不变。                           |
| `envelope`            | `{ attack: 0.09, release: 0.18 }` | 淡入和淡出时间常数，单位为秒。 `false` 禁用包络。       |

`createLipsyncProcessor` 仅支持 `mha` 格式。任何其他格式名称都会抛出错误，并列出可用格式。若要使用其他格式，请传入你自己的 `LipsyncFormatProfile` 来替代格式名称。

{% hint style="info" %}
`getFrameWithAlpha` 已经会对一次发言的前 10 帧和后 10 帧做淡化。当帧经过处理器的包络时，请改用 `getFrame` 读取，这样就不会重复应用淡化。
{% endhint %}

### 推荐增益

使用 `useRecommendedGains` 开启后，这些通道会被缩放。其他所有通道的增益保持为 `1`.

| 通道                                                                                         | 增益     |
| ------------------------------------------------------------------------------------------ | ------ |
| `jawOpen`                                                                                  | `0.72` |
| `jawOpenExtreme`                                                                           | `0.42` |
| `jawLeft`, `jawRight`, `mouthLeft`, `mouthRight`                                           | `0`    |
| `mouthUpperLipRaiseL`, `mouthUpperLipRaiseR`                                               | `0.6`  |
| `mouthLowerLipDepressL`, `mouthLowerLipDepressR`                                           | `0.45` |
| `mouthLipsTogetherUL`, `mouthLipsTogetherUR`, `mouthLipsTogetherDL`, `mouthLipsTogetherDR` | `0.8`  |
| `mouthFunnelUL`, `mouthFunnelUR`, `mouthFunnelDL`, `mouthFunnelDR`                         | `1.15` |
| `mouthLipsPurseUL`, `mouthLipsPurseUR`, `mouthLipsPurseDL`, `mouthLipsPurseDR`             | `1.15` |

该表导出为 `MHA_RECOMMENDED_GAINS`. `createMhaGainMask(overrides)` 会将其返回为一个包含 251 个值的 `Float32Array` 按帧顺序排列，并应用你的覆盖项。

### 让程序动画处理眼部

当你的应用自行眨眼或移动眼睛时，同一通道上的两个写入者会产生闪烁。设置 `skipEyeChannels: true`，或者跳过以下索引： `MHA_EYE_CHANNEL_INDICES` 当你自行写入帧时。它们涵盖以下通道： `eyeBlinkL`, `eyeBlinkR`, `eyeLookUpL`, `eyeLookUpR`, `eyeLookDownL`, `eyeLookDownR`, `eyeLookLeftL`, `eyeLookLeftR`, `eyeLookRightL`, `eyeLookRightR`, `eyeRelaxL`, `eyeRelaxR`, `eyeLowerLidUpL`，以及 `eyeLowerLidUpR`.

### 一次使用一个辅助函数

该处理器由一些你也可以单独调用的辅助函数构成：

| 辅助函数                           | 效果                            |
| ------------------------------ | ----------------------------- |
| `applyMhaLimits(frame)`        | 原地应用这些耦合。                     |
| `symmetrizeMhaMouth(frame)`    | 原地对每一对左右嘴部和下颌进行平均。            |
| `createMhaGainMask(overrides)` | 按帧顺序返回每通道增益。                  |
| `mhaChannelIndex(shortName)`   | 返回某个短通道名称的帧索引，或者 `undefined`. |

### 验证结果

播放一段回复，观察开口元音时的嘴部表现。

{% hint style="success" %}
下颌张开时嘴唇仍贴近牙齿，只有在宽元音时才会露出牙齿，且嘴角两侧会同步移动。
{% endhint %}

### 后续步骤

{% content-ref url="/pages/29f9aac19fcc2f8cd08338eeb8c9340da2c133cf" %}
[使用 blendshape 驱动面部动画](/api-docs/zh/cha-jian-yu-ji-cheng/web-plugins/convai-web-sdk/lipsync-and-blendshape.md)
{% endcontent-ref %}

{% content-ref url="/pages/4478f4a3cec9e37c9839b186fe7a71671f76fa3e" %}
[Blendshape 声道顺序参考](/api-docs/zh/cha-jian-yu-ji-cheng/web-plugins/convai-web-sdk/vanilla-typescript/mappings-reference.md)
{% endcontent-ref %}

{% content-ref url="/pages/22ae6f4a7ad0727295a752b42c53546668e69c5a" %}
[在 React 中添加口型同步](/api-docs/zh/cha-jian-yu-ji-cheng/web-plugins/convai-web-sdk/react/real-time-lipsync.md)
{% endcontent-ref %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/web-plugins/convai-web-sdk/lipsync-and-blendshape/metahuman-limit-drivers-required-for-mha.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
