前言
FaceARSample示例使用Apple的ARKit面部跟踪系统让UE4中的虚拟人物能够模仿真实人物的面部表情和头部旋转。本文主要研究了FaceARSample的工作原理,并对涉及到的相关概念进行了介绍。
示例下载
我们可以在虚幻商城下载示例,网址如下:
https://www.unrealengine.com/marketplace/en-US/product/face-ar-sample
工作流程
通过苹果手机app(Live Link Face)将识别出来的人脸表情结果,通过网络推送给UE4的FaceARSample工程进行模拟虚拟人物表情。
识别数据
苹果手机可以识别52种人脸表情,api地址如下:
https://developer.apple.com/documentation/arkit/arfaceanchor/blendshapelocation
我统计了一下52个表情列表如下:
类别 | 名称 | 描述 |
左眼 | eyeBlinkLeft | 描述左眼上眼睑闭合的系数 |
eyeLookDownLeft | 描述与向下注视一致的左眼睑运动的系数 | |
eyeLookInLeft | 描述左眼睑运动与向右凝视一致的系数。 | |
eyeLookOutLeft | 描述与左眼注视一致的左眼睑运动的系数。 | |
eyeLookUpLeft | 描述与向上凝视一致的左眼睑运动的系数。 | |
eyeSquintLeft | 描述左眼周围面部收缩的系数。 | |
eyeWideLeft | 描述左眼周围眼睑变宽的系数。 | |
右眼 | eyeBlinkRight | 描述右眼上眼睑闭合的系数。 |
eyeLookDownRight | 描述与向下注视一致的右眼睑运动的系数。 | |
eyeLookInRight | 描述与左眼注视一致的右眼睑运动的系数。 | |
eyeLookOutRight | 描述与向右凝视一致的右眼睑运动的系数。 | |
eyeLookUpRight | 描述与向上凝视一致的右眼睑运动的系数。 | |
eyeSquintRight | 描述右眼周围面部收缩的系数。 | |
eyeWideRight | 描述右眼周围眼睑变宽的系数。 | |
嘴巴和下巴 | jawForward | 描述下颌向前运动的系数。 |
jawLeft | 描述下颌向左运动的系数。 | |
jawRight | 描述下颌向右运动的系数。 | |
jawOpen | 描述下颌张开的系数。 | |
mouthClose | 描述嘴唇闭合的系数与下颌位置无关。 | |
mouthFunnel | 描述双唇收缩成张开形状的系数。 | |
mouthPucker | 描述双唇收缩和压缩的系数。 | |
mouthLeft | 描述双唇一起向左移动的系数。 | |
mouthRight | 描述双唇一起向右移动的系数。 | |
mouthSmileLeft | 描述左嘴角向上运动的系数。 | |
mouthSmileRight | 描述右嘴角向上运动的系数。 | |
mouthFrownLeft | 描述左嘴角向下运动的系数。 | |
mouthFrownRight | 描述右嘴角向下运动的系数。 | |
mouthDimpleLeft | 描述左嘴角向后移动的系数。 | |
mouthDimpleRight | 描述右嘴角向后移动的系数。 | |
mouthStretchLeft | 描述左嘴角向左移动的系数。 | |
mouthStretchRight | 描述左嘴角向右移动的系数。 | |
mouthRollLower | 描述下唇向口腔内侧移动的系数。 | |
mouthRollUpper | 描述上唇向口腔内侧移动的系数。 | |
mouthShrugLower | 描述下唇向外运动的系数。 | |
mouthShrugUpper | 描述上唇向外运动的系数。 | |
mouthPressLeft | 描述左侧下唇向上压缩的系数。 | |
mouthPressRight | 描述右侧下唇向上压缩的系数。 | |
mouthLowerDownLeft | 描述左侧下唇向下运动的系数。 | |
mouthLowerDownRight | 描述右侧下唇向下运动的系数。 | |
mouthUpperUpLeft | 描述左侧上唇向上运动的系数。 | |
mouthUpperUpRight | 描述右侧上唇向上运动的系数。 | |
眉毛、脸颊和鼻子 | browDownLeft | 描述左眉毛外部向下运动的系数。 |
browDownRight | 描述右眉外侧部分向下运动的系数。 | |
browInnerUp | 描述两个眉毛内部向上运动的系数。 | |
browOuterUpLeft | 描述左眉毛外侧向上运动的系数。 | |
browOuterUpRight | 描述右眉外侧向上运动的系数。 | |
cheekPuff | 描述双颊向外运动的系数。 | |
cheekSquintLeft | 描述左眼周围和下方脸颊向上运动的系数。 | |
cheekSquintRight | 描述右眼周围和下方脸颊向上运动的系数。 | |
noseSneerLeft | 描述鼻孔周围鼻子左侧抬高的系数。 | |
noseSneerRight | 描述鼻孔周围鼻子右侧抬高的系数。 | |
舌头 | tongueOut | 描述舌头伸展的系数。 |
所有识别出来的参数的值都在0-1之间。那么这些数据是如何驱动人脸变换的呢?这里有一个概念叫blend shapes。
blend shapes
blend shape就是将上述的52个表情,分为初始状态和最终状态,分别对应0和1的值,每个表情的初始状态是一致的。然后通过初始状态和最终状态的混合实现中间状态,通过下面的视频来说明值在0-1之间的变化,对表情的影响。
FaceARSample解读
FaceARSample工程获取到苹果识别出来的52个表情值之后就可以驱动虚拟人物表情了。整个驱动又分为三大部分:
1 blend shape驱动 :实现脸部表情
2 骨骼姿势驱动:配合脸部表情的姿势预设
3 骨骼动画:实现脸部的旋转。
整个驱动核心是动画蓝图KiteBoyHead_JointsAndBlends_AnimBP,在Animations文件夹下面。这个对象在Blueprint中的Animation Graphs中整合了上面3大变换。或者可以说其他代码都不需要看,只需要将这个对象看明白即可。
Skeleton
在skeleton中可以查看角色的骨架,这里的骨架主要用来实现脸部的旋转。另外苹果手机识别的数据信息会自动反馈到All Curves中去,不过需要名称与苹果api的名称一致。如下图:
Mesh
在mesh中可以查看blend shapes,这里定义的blendshape大概比苹果多出了10个,不过这10个也是由苹果识别出来的结果进行组合生成。
这里我列了个清单,好进行对比。
类别 | 名称(arkit) | 名称(ue新增加) | 描述 |
左眼 | eyeBlinkLeft | 描述左眼上眼睑闭合的系数 | |
eyeLookDownLeft | eyeBlinkLookDownLeft | 描述与向下注视一致的左眼睑运动的系数 | |
eyeLookInLeft | eyeBlinkLookInLeft | 描述左眼睑运动与向右凝视一致的系数。 | |
eyeLookOutLeft | eyeBlinkLookOutLeft | 描述与左眼注视一致的左眼睑运动的系数。 | |
eyeLookUpLeft | eyeBlinkLookUpLeft | 描述与向上凝视一致的左眼睑运动的系数。 | |
eyeSquintLeft | eyeBlinkSquintLeft | 描述左眼周围面部收缩的系数。 | |
eyeWideLeft | 描述左眼周围眼睑变宽的系数。 | ||
右眼 | eyeBlinkRight | 描述右眼上眼睑闭合的系数。 | |
eyeLookDownRight | eyeBlinkLookDownRight | 描述与向下注视一致的右眼睑运动的系数。 | |
eyeLookInRight | eyeBlinkLookInRight | 描述与左眼注视一致的右眼睑运动的系数。 | |
eyeLookOutRight | eyeBlinkLookOutRight | 描述与向右凝视一致的右眼睑运动的系数。 | |
eyeLookUpRight | eyeBlinkLookUpRight | 描述与向上凝视一致的右眼睑运动的系数。 | |
eyeSquintRight | eyeBlinkSquintRight | 描述右眼周围面部收缩的系数。 | |
eyeWideRight | 描述右眼周围眼睑变宽的系数。 | ||
嘴巴和下巴 | jawForward | 描述下颌向前运动的系数。 | |
jawLeft | 描述下颌向左运动的系数。 | ||
jawRight | 描述下颌向右运动的系数。 | ||
jawOpen | 描述下颌张开的系数。 | ||
mouthClose | 描述嘴唇闭合的系数与下颌位置无关。 | ||
mouthFunnel | 描述双唇收缩成张开形状的系数。 | ||
mouthPucker | 描述双唇收缩和压缩的系数。 | ||
mouthLeft | 描述双唇一起向左移动的系数。 | ||
mouthRight | 描述双唇一起向右移动的系数。 | ||
mouthSmileLeft | 描述左嘴角向上运动的系数。 | ||
mouthSmileRight | 描述右嘴角向上运动的系数。 | ||
mouthFrownLeft | 描述左嘴角向下运动的系数。 | ||
mouthFrownRight | 描述右嘴角向下运动的系数。 | ||
mouthDimpleLeft | 描述左嘴角向后移动的系数。 | ||
mouthDimpleRight | 描述右嘴角向后移动的系数。 | ||
mouthStretchLeft | 描述左嘴角向左移动的系数。 | ||
mouthStretchRight | 描述左嘴角向右移动的系数。 | ||
mouthRollLower | 描述下唇向口腔内侧移动的系数。 | ||
mouthRollUpper | 描述上唇向口腔内侧移动的系数。 | ||
mouthShrugLower | 描述下唇向外运动的系数。 | ||
mouthShrugUpper | 描述上唇向外运动的系数。 | ||
mouthPressLeft | 描述左侧下唇向上压缩的系数。 | ||
mouthPressRight | 描述右侧下唇向上压缩的系数。 | ||
mouthLowerDownLeft | 描述左侧下唇向下运动的系数。 | ||
mouthLowerDownRight | 描述右侧下唇向下运动的系数。 | ||
mouthUpperUpLeft | 描述左侧上唇向上运动的系数。 | ||
mouthUpperUpRight | 描述右侧上唇向上运动的系数。 | ||
眉毛、脸颊和鼻子 | browDownLeft | 描述左眉毛外部向下运动的系数。 | |
browDownRight | 描述右眉外侧部分向下运动的系数。 | ||
browInnerUp | 描述两个眉毛内部向上运动的系数。 | ||
browOuterUpLeft | 描述左眉毛外侧向上运动的系数。 | ||
browOuterUpRight | 描述右眉外侧向上运动的系数。 | ||
cheekPuff | 描述双颊向外运动的系数。 | ||
cheekSquintLeft | eyeBlinkCheekSquintLeft | 描述左眼周围和下方脸颊向上运动的系数。 | |
cheekSquintRight | eyeBlinkCheekSquintRight | 描述右眼周围和下方脸颊向上运动的系数。 | |
noseSneerLeft | 描述鼻孔周围鼻子左侧抬高的系数。 | ||
noseSneerRight | 描述鼻孔周围鼻子右侧抬高的系数。 | ||
舌头 | tongueOut | 描述舌头伸展的系数。 |
Animation
这里定义了一个21帧的骨骼动画,每一帧对应于arkit识别出来的值。这里主要配合Blend Shape来工作,有些动作表情的变化需要带来骨骼的变化。
这里统计了一下,一共21个表情的需要相关骨骼的变化。其实只有20个,第一个是初始状态。
名称 |
neutral |
cheekSquintLeft |
cheekSquintRight |
eyeBlinkLeft |
eyeBlinkRight |
eyeLookDownLeft |
eyeLookDownRight |
eyeLookInLeft |
eyeLookInRight |
eyeLookOutLeft |
eyeLookOutRight |
eyeLookUpLeft |
eyeLookUpRight |
eyeSquintLeft |
eyeSquintRight |
eyeWideLeft |
eyeWideRight |
jawForward |
jawLeft |
jawOpen |
jawRight |
AnimGraph
这里通过Live Link Pose将上面的3大技术关联起来,实现驱动虚拟人物表情变化。
Live Link Pose是整个入口,接收苹果手机发送过来的数据,整个过程串行的链路。Modify Curve函数是防止拍摄的手机抖动比较大,导致画面抖动。
接下来通过数据生成新增加的12个表情的值。计算逻辑就是两个值的乘积。具体12个表情,可以见上面新增加的清单。
表情更新完之后,再更新21个骨骼动画的值,通过下面的函数进行。这里对应的动画与上面的Animation对应。
最后再控制人物头的转动,这里对应于前面的骨骼网络。最终就将整个人物驱动运行。
写在最后,上一段测试的视频: