# 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/animation` WebSocket,下发 3D 动画控制数据 - `services/avatar.py` 改为音频到控制参数的驱动服务 - `/avatar/schema` 暴露控制协议说明 - 前端采用 Three.js + GLTFLoader + VRM loader,直接在浏览器渲染 3D 模型 - 支持加载本地 VRM/GLB 文件,也支持加载 URL 形式的 VRM/GLB/GLTF - 若未加载模型,页面会回退到一个调试用的 3D 头像,便于联调口型和头部姿态 当前控制协议字段包括: - `jawOpen` - `mouthClose` - `mouthFunnel` - `mouthPucker` - `viseme_aa` - `viseme_ee` - `viseme_oh` - `headYaw` - `headPitch` - `headRoll` 输出包结构示例: ```json { "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. 推荐的下一步实施顺序 1. 先确定 3D 模型和渲染端技术栈 2. 固定一份 blendshape 映射表 3. 把当前前端调试面板替换成真实 3D 场景 4. 增加时间同步与缓冲策略 5. 再替换掉启发式控制算法,接入更强的 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 服务,都可以在这个边界上继续演进。