山东大学项目实训个人纪实(6)——降低唇形同步性能需求
在上一阶段,我虽然通过全内存流式传输将纯语音的对话延迟降低到了较为理想的水平,但另一个棘手的问题又浮出了水面——唇形同步的性能消耗过大。
之前项目中采用的 Audio2Face 方案依赖深度学习算法,需要调用 GPU 来实时计算口型。这导致系统对硬件配置的要求极高,在我平常使用的笔记本上甚至根本带不动。为了能让这个医患沟通评价系统在更普及的硬件(如普通 CPU 平台)上流畅运行,我不得不寻找一种低开销、低延迟的唇形同步替代方案。
经过权衡,我决定重新调转方向,去攻克之前暂时搁置的 Oculus Lipsync 方案。
1. 重新审视 Oculus Lipsync
虽然 Meta 官方早已经停止了对 Oculus Lipsync 插件的维护,且原版插件根本不支持新版的 UE5.6(原设计主要用于离线动画烘焙),但它的核心算法依然具有极高的应用价值。它的底层逻辑很纯粹:直接实时分析音频的波形,计算出 16 种“音素(Visemes)”的权重。
如果我们能够利用 C++ 把这些计算好的权重实时提取出来,并直接映射到 MetaHuman 的面部曲线上,就能实现一个几乎不占用 GPU 的、超低延迟的纯 CPU 唇形同步方案。
为了解决版本兼容问题,我从 GitHub 社区中找到了由其他开发者维护的 UE5 增强版分支。在将项目转为 C++ 后,我把这个插件引入并重新进行了编译。折腾了一番,终于让它在我的 UE5.6 开发环境中顺利加载并运行了起来。
2. 核心难点:将音频流切片传入插件
插件计算音素的核心是调用 FeedAudio() 函数。由于其底层的 ProcessFrameAsync() 是逐帧进行异步分析的,而我们的音频是连续的输入流,因此不能直接把整段音频塞进去,必须在前端对音频数据进行精细的切片。
为了做到音画同步,我需要根据渲染帧率来计算每一帧应该传递的数据量。
假设系统运行帧率为 30 fps(即单帧时间约为 1/30 秒),我们的音频参数为:采样率 24000 Hz,单声道,16-bit(占 2 字节)。
那么,每一帧需要塞给插件的数据量计算如下:
通过编写一个辅助函数,我让系统以每帧 1600 字节的速率源源不断地向插件输送音频切片。实践证明,这种方式确实能让音素权重的计算与画面渲染保持同步。
3. 如何用音素权重精准驱动 MetaHuman?
拿到实时计算出的 16 种音素权重后,如何让 MetaHuman 的面部做出相应的口型,成了下一个需要攻克的点。在新版的 UE 中,MetaHuman 的面部蓝图结构发生了一些调整。我摸索并对比了两种驱动方案:
方案一:利用 Control Rig 驱动
我尝试引入了 MetaHuman 的头部控制组件 CR_MetaHuman_HeadMovement_IK_Proc。这个方案的设计思路是通过面部绑定控制器,在代码中直接修改面部 51 个控制点对应的曲线值。
但在实际编写代码时,我发现这种方式异常繁琐。只用 16 个音素权重去联动驱动 51 个控制点,映射逻辑非常复杂,而且这种“非直观”的代码修改方式对于后续负责细节微调的美术同学来说较为不友好,极难进行后期调试。因此,我放弃了这条路线。方案二:利用姿势资产(Pose Asset)驱动(最终采用)
既然直接改控制点太痛苦,我便转向了官方提供的 ARKit 姿势映射资产 PA_MetaHuman_ARKit_Mapping。
这个资产包含了烘焙好的 ARKit 标准面部姿势,最关键的是它支持“姿势混合(Pose Blending)”。这意味着,我可以很直观地在引擎里为 Oculus 的 16 种标准音素分别设立一一对应的标准唇形。
在动画蓝图中,我只需要使用 Modify Curve 接口,将插件算出来的音素权重实时赋给这些曲线,就可以非常直接地融合出各种过渡唇形。这种“所见即所得”的交互方式,也大大降低了美术团队调试的门槛。
4. 现状与后续进展
目前,这套基于 Pose Asset 驱动的纯 CPU 实时唇形同步方案,已经在我的本地测试环境中跑通了原型,整体运行较为流畅,性能占用也大幅降低。这初步验证了方案的可行性,至少笔记本电脑不再像之前运行 Audio2Face 那样吃力了。
接下来,我的重心将转移到视觉效果的打磨上。这周我会配合美术团队,针对这 16 种标准音素逐一去精调对应的面部与唇部姿势,优化口型之间过渡的自然度,争取让最终呈现的虚拟人对话体验能够有更进一步的提升。
