Animoji是2017年9月13日苹果发布的新款手机iPhone X上的增强现实(AR)表情包。其使用面部识别传感器来检测用户面部表情变化,同时用麦克风记录声音,并最终生成可爱的3D动画表情符号。2018年6月5日,2018年苹果WWDC开发者大会上,苹果为Animoji引入了一项新技术——舌头感知Tongue Detection
,可以感知用户吐舌头的表情,让表情更可爱。下面是一个demo视频:
我们基于Filament渲染引擎也实现了Animoji能力,下面是两个示例素材demo:
本文重点介绍一下Animoji素材和格式及其在Filament中的实现。
Filament简介
Filament是Google非官方支持的开源跨平台3D引擎渲染库,现在用于Android设备上的Sceneform 库中。它提供了基于物理的3D渲染能力。
Filament的优势:
- 提供了基于物理(PBR)的渲染能力(Gameplay不支持基于物理的渲染)。
- 安装包较小,具体大小见后面详细数据。
- 跨平台(Android, iOS, PC),使用glTF/glb标准作为素材格式,支持各种设计工具导出素材。
- 工具链较完整,部分支持自定义shader素材。
Filament从OpenGL ES 3.0开始支持,目前也有实验性质的Vulkan和Metal实现。下面是一些Filament和Gameplay的渲染效果对比:
Animoji能力技术方案
Animoji能力一般有两种实现方案:
-
直接利用人脸点位作为模型顶点坐标,根据3D人脸检测点位变化直接映射到模型点位变化,实现3D模型跟随人脸表情变化的效果。如下图所示:
这种实现方式的优点在于它可以根据人脸点位完整表现用户表情变化,缺点也很明显:用户人脸点位的位置意义一般比较固定,导致模型设计很单一;人脸点位和模型点位强耦合,需要精心设计人脸点位和模型点位的映射关系,极大地限制了设计师的才能发挥。
-
使用多组模型顶点坐标,根据检测表情值计算加权顶点坐标。我们以张嘴表情为示例,实现效果如下图所示:
其中前两幅图是设计师给到的两组模型顶点坐标,分别对应张嘴表情值为0(最小)和1(最大)时的模型效果。在表情检测SDK获取到张嘴表情值后,我们可以根据张嘴表情值的大小,对前两组顶点坐标加权计算出对应表情值的模型顶点坐标,如图3和图4分别对应张嘴程度为0.3和0.6时计算得到的加权顶点坐标位置对应的模型效果。
这种实现方式的优点是它完全解耦了用户表情值和设计师对素材的设计,可以充分发挥设计师的想象力和创造性,效果表现及模型表情是否丰富则取决于表情检测的表情值是否丰富以及素材模型的复杂程度,表情的精细程度依赖表情检测的准确度和模型准确度。
在AEKit SDK中我们选择了第二种技术方案,因为它可以充分释放设计师的发挥空间。下面就重点介绍一下Filament中Animoji效果的实现方案。
Animoji素材配置及格式
Filament引擎使用glTF/glb
作为模型输入格式。glTF(GL Transmission Format)
号称3D界的JPEG
,是一种标准的3D格式定义规范,用于高效地传输和加载3D场景和模型。glTF
最大限度地减少了3D资源的大小以及解压缩和使用这些资源所需的运行时处理。
下面我们简单介绍一下glTF的格式定义,先看一下结构图:
上图是glTF的一个大概结构,分为三大块:
- .gltf对应的json是一个配置描述,包括该模型的节点层级、材质信息、相机位置、动画等相关逻辑结构。
- bin文件对应这些对象的具体数据信息,包括顶点和索引数据、动画配置和骨骼动画配置数据。大块内容可以以Base64的编码内迁到文件中,方便拷贝和加载,也可以以URI的外链方式,侧重复用性。
- png、jpg等纹理图片。
下面是glTF格式中各个不同配置的一个大纲说明,有兴趣的同学可以详细了解一下各项配置的详细说明。其中我们Animoji能力实现使用的就是红框里标识出来的morph动画配置。
Morph Target配置说明
从版本 2.0 开始,glTF 支持对Mesh坐标的Morph Target
定义。Morph Target
存储特定Mesh属性的位移或偏移。在运行时,这些偏移可以按照不同的权重添加到原始Mesh属性中,以实现对某些Mesh坐标的动画变化。Morph Target
在角色动画中经常使用,例如编码虚拟人物的不同面部表情。
下面是拥有两个Morph Target
的Mesh坐标的简单示例:
{
...
"meshes":[
{
"primitives":[
{
"attributes":{
"POSITION":1
},
"targets":[
{
"POSITION":2
},
{
"POSITION":3
}
],
"indices":0
}
],
"weights":[
1.0,
0.5
]
}