5.8 KiB
5.8 KiB
3D 数字人驱动重构方案
1. 目标变更
原链路:
ASR -> LLM -> TTS -> MuseTalk/图片或视频驱动 -> WebRTC 视频轨 -> 前端播放
新链路:
ASR -> LLM -> TTS -> 音频特征/viseme/blendshape 控制数据 -> 前端 3D 渲染引擎 -> 浏览器展示
核心变化:
- 后端不再负责生成数字人视频帧
- 后端保留实时语音、时序仲裁、字幕、音频下行
- 后端新增 3D 控制流输出能力
- 3D 模型渲染放到前端或独立渲染端
2. 为什么要这样改
MuseTalk 这类方案适合“2D 说话视频合成”,但不适合 3D 模型系统:
- 输出形态是视频帧,不是骨骼或 blendshape 控制数据
- 一旦换 3D 模型,原本的视频生成结果无法直接复用
- 每轮都做视频推理,延迟、GPU 占用、链路复杂度都偏高
- 3D 系统通常需要的是时间序列控制参数,而不是渲染后的像素
因此,3D 化的正确边界是:
- 后端负责“说什么、怎么说、何时说”
- 渲染端负责“模型如何动、如何显示”
3. 本次代码重构内容
当前代码已调整为:
- WebRTC 下行仅保留音频轨
- 新增
/ws/animationWebSocket,下发 3D 动画控制数据 services/avatar.py改为音频到控制参数的驱动服务/avatar/schema暴露控制协议说明- 前端采用 Three.js + GLTFLoader + VRM loader,直接在浏览器渲染 3D 模型
- 支持加载本地 VRM/GLB 文件,也支持加载 URL 形式的 VRM/GLB/GLTF
- 若未加载模型,页面会回退到一个调试用的 3D 头像,便于联调口型和头部姿态
当前控制协议字段包括:
jawOpenmouthClosemouthFunnelmouthPuckerviseme_aaviseme_eeviseme_ohheadYawheadPitchheadRoll
输出包结构示例:
{
"type": "animation_chunk",
"driver": "arkit-blendshape-v1",
"mode": "blendshape_stream",
"schema": "arkit",
"chunk_index": 0,
"total_chunks": 3,
"sample_rate": 24000,
"fps": 25,
"text": "你好,欢迎使用系统。",
"frame_count": 18,
"duration_ms": 720,
"frames": [
{
"seq": 102,
"time_ms": 0,
"speaking": true,
"controls": {
"jawOpen": 0.41,
"mouthClose": 0.66,
"mouthFunnel": 0.27,
"mouthPucker": 0.19,
"viseme_aa": 0.41,
"viseme_ee": 0.24,
"viseme_oh": 0.31,
"headYaw": 0.004,
"headPitch": 0.009,
"headRoll": 0.001
}
}
]
}
4. 当前实现的定位
当前实现是“联调级骨架”,作用是:
- 先把系统边界改对
- 让前后端可以并行开发
- 让 3D 引擎接入方尽快有稳定协议可以消费
它不是最终口型算法,原因很明确:
- 目前控制值来自音频能量和频谱特征
- 没有做 phoneme 强制对齐
- 没有做语言相关 viseme 映射
- 没有结合 3D 模型自身 rig 约束做校准
所以这版适合:
- 打通链路
- 调接口
- 验证时序
- 校验驱动字段
不适合直接作为最终高保真口型方案。
5. 推荐的生产化方案
方案 A:前端 Three.js / React Three Fiber
适合:
- 浏览器直接渲染 WebGL 角色
- glTF/VRM 模型
- ARKit blendshape 或自定义 morph target
建议做法:
- 前端加载 3D 模型
- 建立
controlsKey -> morphTargetInfluence映射表 /ws/animation按时间戳缓冲一小段再播放- 音频继续使用 WebRTC 远端音频轨
- 前端用远端音频播放时间作为动画时钟基准
当前仓库已经按这个方向落地了第一版:
- 静态页面通过 import map 从 CDN 引入
three、GLTFLoader、@pixiv/three-vrm - VRM 优先使用
expressionManager写入aa/ee/oh/ou - 普通 glTF 则尝试把控制字段映射到 morph target
- 头部姿态优先驱动
head/neck骨骼,找不到时回退调试头像
方案 B:Unity/Unreal 渲染端
适合:
- 更复杂的材质、灯光、骨骼、表情系统
- 要求更强的 3D 表现力
建议做法:
- 浏览器只做 UI 和音频交互
- Unity/Unreal 作为单独渲染客户端消费
/ws/animation - 浏览器与渲染端通过流媒体或共享画面集成
方案 C:专用音频驱动服务
适合:
- 追求更准的嘴型
- 后端可以接受增加一个模型服务
可以考虑接入:
- phoneme 对齐模型
- viseme 预测模型
- NVIDIA Audio2Face 类服务
- 自研
audio -> blendshape网络
推荐协议保持不变:
- 外部模型服务可以替换
services/avatar.py - 对外仍输出统一
frames[].controls
6. 推荐的下一步实施顺序
- 先确定 3D 模型和渲染端技术栈
- 固定一份 blendshape 映射表
- 把当前前端调试面板替换成真实 3D 场景
- 增加时间同步与缓冲策略
- 再替换掉启发式控制算法,接入更强的 viseme 模型
7. 接口约束建议
为了后续可替换,建议保持这些约束不变:
- 下行动画通道继续使用
/ws/animation - 每条消息包含
frame_count、fps、frames - 每帧保留
seq和time_ms controls使用稳定字段名,不轻易改动- 新增字段时只追加,不破坏已有字段
8. 风险与注意事项
- 真实 3D 模型的 blendshape 命名不一定和当前字段一致,需要做一次映射层
- 如果模型是骨骼嘴型而不是 morph target,需要把
controls转成骨骼驱动参数 - 浏览器渲染时要注意音频时钟和动画时钟一致,否则会出现“听起来对,但嘴型慢半拍”
- 如果后续改成 phoneme/viseme 对齐,建议在包里增加 phoneme 片段和置信度字段
9. 结论
这次重构的重点不是“把 2D 数字人继续硬改下去”,而是把系统边界调整到适合 3D 模型的形态。
现在的系统已经从“视频合成型数字人”切到了“控制流型数字人”,后续无论接 Three.js、Unity、Unreal,还是专门的 Audio2Face/viseme 服务,都可以在这个边界上继续演进。