乐于分享
好东西不私藏

AI完成多个3D虚拟人表情唇形迁移的深入探索(一)

AI完成多个3D虚拟人表情唇形迁移的深入探索(一)
之前花大量文章介绍过我们的3D数字人表情唇形同步目前有两大方案,一个是骨骼绑定动画,一个是用音素驱动52个ARKIT形态键来完成。但无论是骨骼绑定还是制作形态键,每次有新的模型都需要花大量的时间去绑定、雕刻、修改,成本非常高,因此急需AI来完成这个繁琐的操作过程。我们的目标是制作一个标准的面部骨骼和标准的52个形态键的头部。然后如果有新的人物模型,我们就用AI识别面部特征,把骨骼或者形态键一键迁移到新的人物模型上,这样就大大节省了制作成本。
前面文章提到过,目前我们已经探索出一条通过AI完成从图片到建模、重新布线、重新贴图到骨骼绑定、制作自定义动画等系列流程,整个过程仅需半天时间完成。现在唯一的优化点就是面部表情这块。在国外大神的启发下,我们准备解决这个问题。
已关注
关注
重播 分享 赞
做之前先看两个案例:
1. 用骨骼来驱动表情的案例效果:(vibe-human)
已关注
关注
重播 分享 赞
  • 面部表情系统
    预定义了多种情绪(Neutral、Smile、Angry、Sad、Surprised、Disgusted、Fearful),每个情绪通过精确的骨骼位移(position/worldPosition)和旋转(rotation)数据实现。支持强度(intensity)调节(0-1)。
  • 骨骼变形与调试
    加载人物模型(带变形骨骼),支持骨骼可视化(showBones)、点击选中骨骼查看调试信息(位置、rest pose、delta 等)。
  • 眼部与头部注视
    Eye Look(眼睛跟随目标)和 Focus Lock(头部/颈部跟随 + 眼睛锁定),实现自然的注视行为。
  • 其他交互
    • Wireframe 模式。
    • Transform Controls(可拖拽变换模型)。
    • FOV 调节、OrbitControls。
    • 实时 lerp 动画平滑过渡。
2. 用Blendshapes驱动表情的案例效果:(TalkingHead)
已关注
关注
重播 分享 赞

实时唇同步(lip-sync)、表情、头部/眼睛动作和姿势控制

模型必须兼容 Mixamo rig(骨骼结构)和包含 ARKit + Oculus viseme blend shapes(混合形状,用于唇部变形)。

  • 唇同步(Lip-Sync)机制
    • Viseme 驱动
      使用 Oculus/ARKit 标准的 viseme(视觉音素,如 sil、PP、FF、TH 等,通常 15 个左右)。Viseme 是嘴型形状,对应音素组。
    • 内置语言模块
      (lipsync-*.mjs):针对英语(en)、德语(de)、法语(fr)、芬兰语(fi)、立陶宛语(lt)等。每个模块包含文本 → phoneme(音素)→ viseme 的映射规则,根据单词/音素生成时间序列(visemes、times、durations)。
    • Google TTS 集成
      默认使用 Google Cloud TTS(需支持 SSML <mark> 标签获取 word-level timestamps)。TTS 返回音频 + 时间戳 → 语言模块转换为 viseme 序列 → 实时驱动 blend shapes。
    • speakText
      文本 → TTS 生成音频 + 时间戳 → 唇同步队列处理。
    • speakAudio
      支持外部音频 + 手动提供 words/wtimes/visemes 等元数据,实现任意音频驱动唇同步(如 MP3 + Whisper 转录)。
    • 实时平滑
      使用 Web Audio API(AudioContext、GainNode)、Animation Mixer 和 lerp 插值实现自然过渡。支持 trim(调整 viseme 起始/结束偏移)。
  • 动画与表情系统
    • Blend Shapes
      直接修改 Oculus/ARKit morph targets 实现唇部、眼睛、眉毛等。
    • 骨骼动画
      Mixamo FBX 动画播放、姿势(pose)、手势(gesture,如 thumbup、shrug)。
    • Dynamic Bones + Physics
      dynamicbones.mjs 支持头发、衣服等物理模拟(动态骨骼)。
    • Idle / Speaking 行为
      随机微动作(头部移动、眼神接触比例可调)、情绪(mood:neutral、happy、angry 等)映射到 blend shapes。
    • Eye / Head Tracking
      lookAt、makeEyeContact 等方法控制注视方向。
  • 内置/默认TTS
    Google Cloud TTS(推荐支持 SSML <mark> 的声音,如 Standard/Wavenet/Neural2)。提供 word-level timestamps,便于 viseme 生成。支持多语言,通过代理/JWT/Apikey 配置。
  • 外部 TTS(推荐生产使用)
    • ElevenLabs WebSocket API
      实时流式,支持高质量声音。
    • Microsoft Azure Speech SDK
      直接输出 viseme 或 blend shapes,支持 100+ 语言,无需额外 lipsync 模块。
    • HeadTTS
      (官方 add-on):完全浏览器内运行的免费开源 TTS(基于 Kokoro/StyleTTS 2 等神经声音),提供 phoneme-level timestamps + viseme IDs。WebGPU 支持,无 API 费用。
  • 其他集成
    • OpenAI、Gemini、Grok 等 LLM 生成文本 → 任意 TTS。
    • HeadAudio
      (官方 add-on):纯音频驱动唇同步(Audio Worklet + MFCC 特征提取),无需文本/时间戳,适用于任何音频流(实时 speech-to-speech)。
    • 本地/自建:Whisper 转录 + 本地 TTS 等。
上面两个案例一个是用骨骼做表情,一个是用BS做表情,各有优劣。接下来准备基于下面思路来迁移骨骼和BS。
迁移BS:
将 ARKit blendshapes 从一个任意的人形网格转移到另一个网格上——无需手动放置地标或权重绘制。

在底层,它使用 Sapiens2 模型来预测顶点组,这些顶点组作为两个网格之间的对应关系,然后我们使用 变形转移(deformation transfer) 将 blendshapes 从一个网格重新映射到另一个。然后做逆向的面部骨骼绑定

实现思路(针对 Blender 插件,将 52 个 ARKit BS 复制/转移到另一个没有 BS 的 Mesh)

这个工具的核心是无监督/半自动的 blendshape 转移,特别适合将一个已有 ARKit 52 个 blendshape 的标准面部网格(source)转移到另一个拓扑不同的人形面部网格(target)上。你可以用 Python + Blender API 来实现类似插件。

1. 核心流程(基于原帖描述)

  1. 建立顶点/面片对应关系(Correspondences)
    • 使用 Sapiens2(Meta 的高分辨率人体视觉模型)预测 vertex groups 或 dense correspondences(如 body-part segmentation、normals、pointmap)。
    • Sapiens2 可以输出高精度的语义分割和几何预测,帮助在两个拓扑不同的网格上找到“眼睛对应眼睛、嘴巴对应嘴巴”等区域,即使顶点数量和连接方式完全不同。
  2. 变形转移(Deformation Transfer)
    • Deformation Gradient Transfer(Sumner & Popović 等经典论文)。
    • 优化求解每个对应三角面的线性变换。
    • 经典技术:对于 source 的每个 blendshape(相对 neutral 的变形),计算 deformation gradients(仿射变换:旋转+缩放+剪切 + 平移),然后应用到 target 网格上对应的三角面。
    • 最小化源和目标变形之间的差异,同时保持 target 网格的局部形状(local detail preservation)。
    • 常见数学方法:
  3. 生成 target 的 52 个 Blendshapes
    • 对 source 的每个 ARKit BS,应用转移后得到 target 的对应形状键(Shape Key)。
    • 结果:target 网格获得 52 个命名正确的 Shape Keys,可直接用于 ARKit / Live Link Face 驱动。

2. 在 Blender 中如何实现(实用建议)

前提准备:

  • Source 网格:已有 52 个 ARKit Shape Keys(标准命名,如 eyeBlinkLeft、mouthSmileRight 等)。
  • Target 网格:只有 neutral 基础形状,人形面部拓扑(不一定相同顶点数)。

实现步骤(插件架构):

步骤 1:开发 Blender 插件基础 UI 和数据读取

你需要创建一个侧边栏面板,让用户选择“源模型”和“目标模型”。

codePython

import bpyclassVIEW3D_PT_BSTransfer(bpy.types.Panel):    bl_space_type = 'VIEW_3D'    bl_region_type = 'UI'    bl_category = "AI BS Transfer"    bl_label = "Blendshape Transfer"defdraw(self, context):        layout = self.layout        scene = context.scene        layout.prop_search(scene, "source_mesh", scene, "objects", text="Source")        layout.prop_search(scene, "target_mesh", scene, "objects", text="Target")        layout.operator("object.transfer_bs", text="Transfer Blendshapes")

步骤 2:使用 AI 获取特征对齐点 (Sapiens2 / MediaPipe)

难点:AI 视觉模型通常处理的是 2D 图像,而不是 3D Mesh。工程实现方法:

  1. 在代码中自动在正前方创建一个正交摄像机(Orthographic Camera)。

  2. 渲染源模型和目标模型的前视图深度图或法线图。

  3. 将图像送入 AI 模型(例如 MediaPipe Face Mesh 可输出 468 个面部特征点)。

  4. 反向投影 (Raycast):将 2D 特征点通过摄像机射线打回到 3D Mesh 上,获取对应的 3D 坐标和顶点 ID。

步骤 3:核心算法 - 形变传递 (Deformation Transfer)

如果严格按照图形学论文《Deformation Transfer for Triangle Meshes (SIGGRAPH 2004)》实现,需要解拉普拉斯方程。但为了在 Python 中快速实现,径向基函数插值 (RBF Interpolation) 是一种非常有效且容易用 SciPy 实现的替代方案:

你可以根据两组特征点的对应关系,使用 RBF 构建一个空间变形场。

codePython

import numpy as npfrom scipy.interpolate import RBFInterpolatordefcalculate_deformation(source_base_verts, target_base_verts, source_shape_verts):# 1. 计算源模型当前Shape Key的顶点位移 (Delta)    delta_source = source_shape_verts - source_base_verts# 2. 使用特征点建立 RBF 插值函数# 注意:这里为了简化,假设已经做好了粗略的顶点对应。# 实际工程中,这里传入的应该是 AI 识别到的关键点对应的坐标。    rbf = RBFInterpolator(source_landmarks, delta_source_landmarks, kernel='thin_plate_spline')# 3. 将目标模型的所有顶点送入变形场,预测它们的位移    delta_target = rbf(target_base_verts)# 4. 计算出目标模型新Shape Key的顶点绝对坐标    target_shape_verts = target_base_verts + delta_targetreturn target_shape_verts

步骤 4:将计算结果写回 Blender Shape Keys

遍历源模型的 52 个形态键,计算变形后,利用 Blender API 创建目标模型的形态键。

codePython

defapply_shape_keys(source_obj, target_obj):# 确保目标模型有 Basis 形态键ifnot target_obj.data.shape_keys:        target_obj.shape_key_add(name="Basis")    target_verts = np.array([v.co for v in target_obj.data.vertices])# 遍历源模型的所有形态键for sk in source_obj.data.shape_keys.key_blocks:if sk.name == "Basis":continue# 获取源模型的形变坐标        source_sk_verts = np.array([v.co for v in sk.data])# --- 在这里调用 步骤3 的机器学习/插值算法 ---# new_target_verts = run_ai_deformation_transfer(source_obj, target_obj, source_sk_verts)        new_target_verts = ... # 在目标模型上创建新的形态键        new_sk = target_obj.shape_key_add(name=sk.name)# 将新坐标写入目标形态键for i, vert inenumerate(new_target_verts):            new_sk.data[i].co = vert
  1. 后续优化(下一步骨骼绑定)
    • 生成 BS 后,可进一步提取骨骼 rig(自动放置 jaw/eye/lip bones,根据 landmarks 或预测的 vertex groups)。
    • 支持 corrective shape keys 解决穿模/ artifact。

3. 现有 Blender 资源可参考/复用

  • ARKit Blendshape Helper 等插件(自动添加 52 个 Shape Keys)。
  • ShapeKeyGen Pro 等工具(一键生成 ARKit BS)。
迁移骨骼:

在Blender中,将一个已有的面部骨骼(Armature)和绑定(Rigging)转移到一个全新的、没有绑定的头部模型上,是角色制作中非常常见的操作。

由于两个头部的长相、大小和拓扑(布线)通常不一样,你不能直接“生搬硬套”。标准的行业工作流分为三步:对齐网格 -> 调整骨骼位置 -> 传递顶点权重(Weight Transfer)。


准备工作:位置重合

将“源头部(有骨骼的)”和“目标头部(无骨骼的)”放在同一个场景中。

缩放并移动目标头部,使其大小和位置尽可能与源头部完全重合(眼睛对眼睛,下巴对下巴)。


第一步:适配骨骼到新脸庞

因为两张脸的长相不同,你需要把骨骼的位置稍微挪一挪,匹配新脸的五官。

1.复制骨架:为了不破坏原文件,选中源模型的骨架(Armature),按 Shift + D 复制一份,作为目标头部的新骨架。

2.显示在最前:选中这个新骨架,在右侧属性面板的 “物体数据属性(绿色小人图标)” -> “视图显示 (Viewport Display)” 中,勾选 “在最前 (In Front)”,这样你能看清骨骼。

3.调整骨骼位置:

○选择新骨架,按 Tab 进入 “编辑模式 (Edit Mode)”。

○打开 “X轴对称”(在视口右上角的蝴蝶图标,或者按 N 侧边栏的 Tool 里勾选 X-Axis Mirror)。

○手动拖拽骨骼的节点,使其贴合目标头部的五官(比如把下颌骨对准新下巴,眼球骨对准新眼球的中心,嘴唇骨移到新嘴唇的边缘)。


第二步:传递权重(最核心的一步)

这一步是把旧模型上的“哪根骨头控制哪块肉”的信息,自动映射给新模型。即使两个头部的布线(拓扑)完全不同,Blender也能通过空间位置“猜”出来。

添加修改器

  1. 在 物体模式 (Object Mode) 下。

  2. 只选中你的目标头部(那个没有骨骼的新Mesh)。

  3. 去右侧属性面板,点击 “修改器属性”(蓝色的扳手图标)。

  4. 点击“添加修改器 (Add Modifier)”,在列表中找到并添加 “数据传递 (Data Transfer)”。(在 Blender 4.0+ 中,它可能在 编辑 (Edit) 分类下)。

设置参数(跟着填即可)在添加好的“数据传递”修改器面板中,进行以下设置:

  1. 源物体 (Source):用吸管吸取(或在下拉菜单选择)你的 源头部(旧模型)。

  2. 勾选 顶点数据 (Vertex Data)。

  3. 展开“顶点数据”的折叠菜单,勾选里面的 顶点组 (Vertex Groups)。

  4. 在下方的 映射 (Mapping) 选项中,点击下拉菜单,选择 “最近面插值 (Nearest Face Interpolated)”。(这一步很关键,能保证不同布线的模型传递准确)。

第 3 步:生成并应用

  1. 在修改器面板的底部,点击那个长长的按钮:生成数据层 (Generate Data Layers)。(点击后貌似没发生什么,但其实内部数据已经传过去了)

  2. 确认无误后,应用 (Apply) 这个修改器。(鼠标放在修改器上按快捷键 Ctrl + A,或者点击修改器名称旁边的小箭头选择“应用”)。

完成这一步后,新模型的数据面板里(绿色三角图标 -> 顶点组 Vertex Groups),就会自动生成所有骨骼的名字和权重数据!


第三步:将新模型绑定到新骨架

现在新模型已经有了权重,我们只需要把它和刚才调整好的新骨架“连”起来。

1.回到 “物体模式 (Object Mode)”。

2.先选中 目标头部模型(新Mesh)。

3.按住 Shift 键,选中刚才调好位置的 新骨架 (Armature)。

4.按下快捷键 Ctrl + P 打开父级菜单。

5.选择 “骨架形变 (Armature Deform)”。

(⚠️警告:千万不要选“带自动权重”,因为我们已经在第二步精确传递了旧模型的权重!选第一项普通的骨架形变即可)


第四步:检查与修补

现在,你的新头部已经被骨骼驱动了!你可以选中骨架,进入 “姿态模式 (Pose Mode)”,旋转下巴、眼睛的骨骼,看看新脸是否跟着动。

常见问题与修补建议:

●嘴唇内部粘连/拉扯:因为面部传递是基于空间距离的,如果新旧模型的嘴唇厚度不同,上下嘴唇的权重可能会互相串门(比如张嘴时下嘴唇拉扯上嘴唇)。

●如何修复:选中新模型进入 “权重绘制模式 (Weight Paint)”,在右侧的“顶点组”里找到上下嘴唇的骨骼名字,使用左侧工具栏的 “减淡/相减 (Subtract)” 画笔,把错误粘连部位的权重擦除掉。

●眼睛/牙齿不跟着动:眼睛和牙齿属于刚体,通常不需要平滑过渡的权重。建议进入编辑模式,全选眼球顶点,在右侧顶点组里找到对应的眼球骨骼,点击 “指定 (Assign)” 权重为 1.0。

通过这套流程,你可以把任何优秀的骨骼绑定系统(例如Rigify面部、Auto-Rig Pro或者你自己做的骨架)移植到无数个不同的角色头上,而不需要每次都从零开始刷权重!

写在最后
这个实现后将对目前的3D数字人流程进一步简化,直接让我们的模型师解放双手。有对这个项目感兴趣的欢迎评论区交流,项目的开发速度完全来自大家互动的动力。
如何联系我们?
公众号后台私信我们。如果后台在1天内没有回复,就可能被系统屏蔽了,想添加我们微信的,可以加入知识星球获取微信号和体验群进一步沟通。
关于作者
做一只爬的最久的乌龟,保持学习保持好奇,即使慢一点,遇到一点困难,只要最后能到达终点,又有什么关系呢。
毕竟人生没有白走的路,每一步都算数。

https://vibe-stack.github.io/vibe-human/

https://github.com/vibe-stack/vibe-human
https://github.com/met4citizen/TalkingHead/

相关学习资料