# VRCFaceTracking:面捕从入门到入土 # =====正在施工===== # 前期准备 在开始我们的内容前,我将列举本文用到的、可以参考的页面 1. [VRCFT-Github](https://vrcft.io/) 2. [VRCCF-官方文档](https://docs.vrcft.io/) 3. [VRCFT-UnifiedExpressions图片对照表](https://docs.vrcft.io/docs/tutorial-avatars/tutorial-avatars-extras/unified-blendshapes) 4. [VRCFT-不同标准的形态键名称对应表](https://docs.google.com/spreadsheets/d/118jo960co3Mgw8eREFVBsaJ7z0GtKNr52IB4Bz99VTA/edit?pli=1&gid=0#gid=0) 5. 6. 需要注意的是,本文第三部分内容可能会以部分成品面捕(Booth商品等)作为例子,各个商品的具体内容不同,仅供参考 # 1. 硬件层面 # 关于设备 目前来说市面上有很多的方案,这里简单列举一些我所知道的设备。
接下来基本会以ViveProEye(默认安装ViveFacialTracker)、QuestPro和Pico4Pro来展开介绍,因为这三款设备代表了三个不同数据标准,同时还是非常主流的头显。
品牌/厂家 | 设备名称 | 眼部 | 面部 | 数据标准 | 备注 |
**HTC** | **Vive Pro Eye** | **√** | **-** | **SRanipal** | **搭配FacialTracker实现面捕捕捉** |
HTC | Vive Focus 3 | - | - | SRanipal | 搭配特定设备实现面捕眼追捕捉 |
HTC | Vive XR Elite | - | - | SRanipal | 搭配特定设备实现面捕眼追捕捉 |
HTC | Vive Focus Vision | √ | √ | 未知 | 未发售,待更新 |
**HTC** | **Vive Facial Tracker** | **-** | **√** | **SRanipal** | **通用配件(Type-c有线连接)** |
HTC | Elite XR全脸识别追踪器 | √ | √ | SRanipal | 仅适配 XR Elite |
HTC | Focus3面捕/眼球追踪器 | √ | √ | SRanipal | 仅适配 Focus3 |
**Meta** | **Quest Pro** | **√** | **√** | **FACS Reduced** | |
**Pico** | **Pico 4 Pro** | **√** | **√** | **ARKit** | |
Pico | Pico 4 Enterprise | √ | √ | ARKit | |
Varjo | Varjo Aero | √ | - | 未知(Tobii) | |
Varjo | Varjo XR-3 | √ | - | 未知(Tobii) | |
Varjo | Varjo VR-3 | √ | - | 未知(Tobii) | |
Pimax | Crystal | √ | - | 未知(Tobii) | |
Pimax | Droolon Pi 1 | √ | - | SRanipal | |
Apple | iPhone(深度摄像头) | √ | √ | ARKit | |
Android | Android Phone | √ | √ | 未知 | 需要使用Meowface app |
关于“标准”是什么,有什么区别,请看下一页
同时,还有一部分自制设备,可以搭配在Quest2/3、Index等设备上,需要较强的动手能力,同时在制作使用这些设备的时候,请注意安全,这里可以提供站内的一个自制方案: ### [Quest3面罩面捕方案(cymple) | VRCD 文档库](https://docs.vrcd.org.cn/books/quest3cymple) # 各个面捕数据的标准 上一页说到,目前绝大部分面捕使用者都是ViveProEye、QuestPro、Pico4Pro,对应了SRanipal、FACS Reduced和ARKit三种标准,他们之间有简单的一些差距。 详细的表格则在[Face Tracking Shapes Conversion](https://docs.google.com/spreadsheets/d/118jo960co3Mgw8eREFVBsaJ7z0GtKNr52IB4Bz99VTA/edit?pli=1&gid=0#gid=0),简单的表述如下:眉毛相关数据 | 舌头伸出 | 舌头的上下左右移动 | |
SRanipal | × | √ | √ |
FACS Reduced | √ | √ | × |
ARKit | √ | √ | × |
VRCFT,全称VRCFaceTracking,是一个无论什么面捕设备都能够使用的软件,这里先不展开对软件的介绍,我们需要了解的是VRCFT专用的标准:**UnifiedExpressions(UE)**
VRCFT其实也就是通过使用不同的模块将不同设备的面捕标准统一成 **UnifiedExpressions(下称UE)**,可以看作是一种抽象。这种抽象过程本质上是在不同的底层数据和协议上,提取出共性并进行整合,形成一个统一的接口和表达方式,方便开发者和用户在不同设备上获得一致的体验。 之后我们无论是讨论何种设备,以及后面的模型制作过程,都只是使用这个UE标准,这样就能保证我们做出来的模型能够在不同设备上使用了~ # 2. 软件层面 # 软件下载及安装 本页将介绍安装VRCFaceTracking(下称VRCFT) 首先打开VRCFT的Github,在Releases中选择版本进行下载,.msix和.appinstaller都是可以的。[VRCFT-Github](https://github.com/benaclejames/VRCFaceTracking)一般来说,推荐最新版本的VRCFT,但目前的问题是5.2.3.0中Force Relevancy没有实际效果,得回退到5.1.1.0,这就导致如果想使用Av3Emulator与VRCFT在Unity中使用OSC直接测试面捕效果的话,不能使用5.2.3.0。这条建议仅供拥有特殊需求的朋友参考。
前文提过,我们需要一个模块来把我们不同标准转化成UE标准,在标注处下载对应的模块,或从此处安装本地模块包。 [](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/FOzJmBJ10fuoTkd3-image.png)有部分模块也许需要安装后重启VRCFT,若完全没有变化请尝试重启
[](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/ZbwVgRPBmJrgU8hc-image.png) 初始化阶段正常安装和开启后,此处应该在Initializing Modules,随后进入对应模块效果而不是显示No Modules
[](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/JXNbOxBmbCU6eO9i-image.png) 模块正常加载阶段如果您中途遇到了VRCFT的安装、使用问题或模块的加载出现了错误,请看本章节下一页。
##### 如果您的设备正常接入了VRCFT,一切都非常棒,那接下来我们需要做的就是制作模型了,请开始第三部分-模型层面。 # 遇到的问题及解决方案 关于Pico互联与VRCFT相关问题,站内有详细解答: #### **[PICO 互联使用 VRCFaceTrack... | VRCD 文档库](https://docs.vrcd.org.cn/books/pico-vrcfacetracking)** TBD # 3. 模型层面 # 基本原理 ##### **简易流程图** 下面是一个从现实动作到模型动作的简单流程图,仅供参考,实际情况可能会有所变化。 [](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/CRzEwycMOx08PltF-image.png) 简单来说,头显发送原始数据到VRCFT,然后通过VRCFT转化UE参数到模型当中,模型通过动画器控制动画驱动模型形态键实现动作。 ##### **参数的平滑和参数二进制压缩** 根据VRChat的文档和我们自己游玩的经验,我们可以知道VRC的参数同步是有延迟的,一般会隔0.2s左右会向远端(也就是其他玩家)同步一次同步参数。例如我的GestureRightWeight这个float的参数,我在一秒内将他从0变到1,在本地会看到这个过程很平滑,而远端其他玩家就会出现段落感,如图,下面的滑条为这个参数本地变化,上面为远端看到的这个参数的变化情况 [](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/gUGZfrEjrMmgJ8OG-ezgif-2-63d50de718.gif)即时是本地,也有可能出现数据跳跃,为了避免这个情况,所以我们现在需要将这个参数更好的流畅的同步到远端,并且本地也做一定的平滑处理,常用的平滑处理就是使用内插运算,简单举个例子,就是一个参数从0,在变成0.2的时候,取中值0.1,然后变化到0.3的时候,取得中值0.2,无时无刻进行这个运算,就会平滑的处理参数变化。这整个流程一般称作Smooth。 这时候就需要[OSCmooth](https://github.com/regzo2/OSCmooth)的作用,我们留在后文讲。 同时,因为面捕涉及的形态键众多,如果每一个形态键都由一个对应的float值来操控的话,如果是ARKit,那就需要52\*8=416个参数,UE只会更多,众所周知VRC SDK的话只允许有256个同步参数,这时候我们就需要对参数进行一些压缩,针对一些不需要那么精细的动作,进行二进制压缩。这个流程一般称作Binary。 二进制压缩的基本原理可以理解为,如果我们之前使用一个int参数来表达0、1、2、3、4、5、6、7,总共8个值,占用了8个参数空间,而我们现在使用3个bool参数来实现相同的表达,以下用1和0来表达一个bool的状态,可以得出这个对照表:int | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
bool | 000 | 001 | 010 | 011 | 100 | 101 | 110 | 111 |
float | 0 | 0.143 | 0.286 | 0.429 | 0.571 | 0.714 | 0.857 | 1 |
bool | 000 | 001 | 010 | 011 | 100 | 101 | 110 | 111 |
如果增加Bool的数量,能够表达的int值就会更多,float值就会更加细致,但过度增加Bool不仅会徒增参数消耗,同时还收益不高
了解了这些,我们就可以准备接下来的内容了 # 动画器详解 我们现在以Jerry的Template来作为示范的例子,因为从0开始创造有一定难度,所以我们不妨先来看看成品是怎么样的。 Jerry的Template地址在这里:[VRCFT-Template](https://github.com/Adjerry91/VRCFaceTracking-Templates) 在早期版本,这个模板是直接在Asset目录下的,但在5.0版本之后,则需要在Package中寻找VRCFT - Jerry's Templates值得一提的是,Jerry的模板是使用VRC Fury来安装的,最方便的办法其实是用VRCFury的预制件直接丢入模型当中,但我们只是为了分析动画器,所以就直接使用资产中的Animator啦
Jerry的模板每一个版本都有可能发生一些排版、参数命名上的变化,所以本版本(6.2.1)仅供参考,但万变不离其宗,我们需要学的是里面的内容而不是冷冰冰的模仿出一个一模一样的面捕控制器
[](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/JX2diEDuwbnp14mm-image.png) [](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/tSNeWZsC62bU7uZL-image.png) 在目录"VRCFT - Jerry's Templates\\Animators\\"中有各种标准的动画器,我们之前说过我们只使用UE标准,我们点击进入UE对应的Animator,FX - Face Tracking就是我们的对象啦。 #### **图层Layers**  正如我说的,不同版本之间差异很大,左图为6.2.1版本的Template,右图为6.3.3版本,他将左图中下面所有的图层整合进同一个图层了,方便分开学习,我们还是先使用左图来进行讲解
##### 各个图层Layers的含义如下: Tracking\_State:用于调整Avatar的追踪状态,主要使用AnimatorTrackingControl组件,组件文档详见[State Behaviors | VRChat Creation](https://creators.vrchat.com/avatars/state-behaviors/#animator-tracking-control) Face\_Tracking\_Blendshape\_Driver:用于直接驱动模型的形态键,**下称Driver图层** Face\_Tracking\_OSCmooth\_Binary\_Gen:用于对参数进行二进制压缩,详见[基本原理 | VRCD 文档库](https://docs.vrcd.org.cn/books/vrcfacetracking/page/a8f15),**下称Binary图层** Face\_Tracking\_Frame\_Time\_Counter:用于同步帧时间,缓解极高和极低帧率下的特殊情况 Face\_Tracking\_OSCmooth\_Smoothing\_Gen:用于对参数进行平滑处理,详见[基本原理 | VRCD 文档库](https://docs.vrcd.org.cn/books/vrcfacetracking/page/a8f15),**下称Smooth图层** #### **参数Parameters** [](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/hci8FLQvI4rFVExr-image.png)[](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/Pbow6IwPZjHiUV05-image.png) ##### 各个参数Parameters的含义如下:参数名 | 动画器/本地 | 参数表/远程同步 | 描述 |
EyeTrackingActive | Float | Bool | 眼球追踪开启 |
LipTrackingActive | Float | Bool | 面部追踪开启 |
EyeDilationEnable | Float | Bool | 瞳孔追踪开启(瞳孔放大缩小,仅Tobii系可用) |
FacialExpressionsDisabled | Bool | Bool | 关闭手势表情 |
VisemesEnable | Bool | Bool | 口型同步开启 |
FT/v2/xxxxxx | Float | - | 原始输入的浮点值 |
FT/v2/xxxxxx1;FT/v2/xxxxxx2;FT/v2/xxxxxx4 | Bool | Bool | 原始输入的布尔值,之后会经由Binary转换为BinaryOut/FT/v2/xxxxxx |
BinaryOut/FT/v2/xxxxxx | Float | - | 经过Binary后的值,之后会经由Smooth转换为OSCm/Proxy/FT/v2/xxxxxx |
OSCm/Proxy/FT/v2/xxxxxx | Float | - | 经过Smooth后的值,之后会在Driver图层直接驱动形态键 |
OSCm/BlendSet | Float | - | 混合树同时进行的关键参数,请保持为1 |
OSCm/Local/FloatSmoothing | Float | - | 本地参数的通用平滑程度,越接近1平滑程度越高 |
OSCm/Local/PupilDilationSmoothing | Float | - | 本地参数的瞳孔平滑程度,越接近1平滑程度越高 |
OSCm/Remote/EyeLidSmoothing | Float | - | 远程参数的眨眼平滑程度,越接近1平滑程度越高 |
OSCm/Remote/FloatSmoothing | Float | - | 远程参数的通用平滑程度,越接近1平滑程度越高 |
OSCm/Remote/PupilDilationSmoothing | Float | - | 远程参数的瞳孔平滑程度,越接近1平滑程度越高 |
小Tips: 在动画器页面单击F,可以显示全部的状态
双击Face\_Tracking\_BlendTree可以进入混合树BlendTree页面 [](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/5in0XuDbvJmH4Fd5-image.png)在6.3.0版本之后则是在FT Blendshape Driver中,同样是双击点开,这里是最后一次做关于更高版本的Template的解释,之后的请参考混合树节点名称来自行判断图层。
[](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/mjgLfKkEy5HIAvrI-image.png) ##### 各种混合树简易介绍 [](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/kxc3UotqG0P8LXby-image.png)[](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/76OaFEbY972r4nhf-image.png) **Direct类型**,则代表一旦满足**Parameter**中填入的参数,则会直接进入下一步,如果同时满足很多各**Motion**,则同时进入,例如我已经打开了LipTracking,也就是嘴部追踪,所有与嘴相关的,例如JawOpenBlend会运行。 接下来我们把目标聚集在Jaw Open Blend和Mouth Closed Blend,这是最简单的**1D类型**混合树 [](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/fY7NmHUdhLTnDWpJ-image.png) 我们先从简单的看起,MouthClosedBlend中右上角**Parameter**,填入了OSCm/Proxy/FT/v2/MouthClosed,说明由这个参数决定以下的**Motion**,而**Threshold(阈值)**也就是当这个参数到达多少的时候进入这个**Motion**,当OSCm/Proxy/FT/v2/MouthClosed=1时,执行动画Mouth\_Closed,参数=0时,执行MouthClosed 0。 [](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/pTmSEyAcHuz40JDT-image.png) 而这两个动画也非常好理解,Mouth\_Closed 0让Body的MouthClose形态键为0,Mouth\_Closed让让Body的MouthClose形态键为1,我们需要知道,**Blendtree是能够提供中间过渡态的**,也就是说,当参数=0.5的时候,也就是在这两个状态的中点,那么形态键就会是50。 [](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/deF1qd2ggHzkvpqh-image.png)[](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/Qr4ad0um79qTF3zN-image.png)根据对阈值的简单了解,我们可以知道,参数\_A从0变到1的时候,形态键数据是从0变到100,那么如果想要修改一个动作,让动作更加灵敏,则可以通过**降低阈值**,来让形态键更快的达到100。
举个例子,如果我把MouthClosed的阈值从1改成了0.7,那么在参数=0.7的时候,形态键=100;参数=0.35的时候,形态键=50,这样能够让形态键更快的到最大值,当然这样也会导致更加的灵敏,缺点就是,参数只要大过0.7,形态键都是在100,不会变。
而JawOpenBlend右上角是使用的OSCm/Proxy/FT/v2/JawOpen,那么,当参数=0时,则进入JawOpenHelper这个**Motion**,参数=1时,进入Jaw\_Open这个动画,而JawOpenHelper这个**Motion**,是另一层**嵌套上去的Blendtree**。 [](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/dSBynWLNXW2GV69U-image.png) [](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/d9O1x349VcNiRL9l-image.png) 而这个Helper也是同样的**1D混合树**,控制Jaw\_Open相关动画。所以我们可以知道,为了实现形态键JawOpen=1,有几个路线: 1. OSCm/Proxy/FT/v2/JawOpen=1 2. OSCm/Proxy/FT/v2/JawOpen=0 但是 OSCm/Proxy/FT/v2/TongueOut=4 **这个的思路就是:当我的设备检测到我的JawOpen打开了,那就是真的打开了,直接让模型的JawOpen也为1;但是有的时候我伸舌头TongueOut=1了,如果这时候设备检测到我的JawOpen其实为0,如果我直接伸出舌头会穿模,所以我让他用舌头的参数控制一点点的JawOpen,让嘴巴微微打开,让舌头伸出来不穿模。** 还有一些Blendtree使用的参数是有负数值的,例如MouthX一个参数即可表示左和右 [](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/37Z2LFEW9LkQPEcd-image.png) 接下来是**2D类型**的混合树,图上所示的是右眼球的移动轨迹,参数为OSCm/Proxy/FT/v2/EyeRightX和OSCm/Proxy/FT/v2/EyeRightY,这说明有两个参数同时控制了这个混合树,而PosX则是对应左边那个参数的设置,PosY对应的则是右边,我们不难理解每一个位置对应的动作。 [](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/4FduIudYgqImkpBf-image.png)值得一提的是,这种类型的混合树录制动画的时候请让其他相冲的形态键为0
[](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/2J2NOTzT9SYRnV4s-image.png) [](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/q6wmdxOxdvAlcGtr-image.png) ##### 关于其他2D混合树,这里贴出Unity官方的文档进行解释: \_\_2D Simple Directional\_\_:最好在运动表示不同方向(例如“向前走”、“向后退”、“向左走”和“向右走”或者“向上瞄准”、“向下瞄准”、“向左瞄准”和“向右瞄准”)时使用。根据需要可以包括位置 (0, 0) 处的单个运动,例如“空闲”或“瞄准”。在 Simple Directional 类型中,在同一方向上\_不\_应该有多个运动,例如“向前走”和“向前跑”。 \_\_2D Freeform Directional\_\_:运动表示不同方向时,也使用此混合类型,但是您可以在同一方向上有多个运动,例如“向前走”和“向前跑”。在 Freeform Directional 类型中,运动集应始终包括位置 (0, 0) 处的单个运动,例如“空闲”。 \_\_2D Freeform Cartesian\_\_:最好在运动不表示不同方向时使用。凭借 Freeform Cartesian,X 参数和 Y 参数可以表示不同概念,例如角速度和线速度。一个示例是诸如“向前走不转弯”、“向前跑不转弯”、“向前走右转”、“向前跑右转”之类的运动。 ### **2. Binary图层内容详解** [](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/oYTY3KE7V9slqKDq-2024-09-19-23-29-06-1.gif) 图上就是一个Binary的最终实现的效果,通过输入TongueOut1、TongueOut2、TongueOut4,输出不同的BinaryOut/FT/v2/TongueOut值,最后经过平滑转为OSCm/Proxy/FT/v2/TongueOut 那接下来我们一样是双击进入混合树,开始我们的Binary部分 [](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/4XUfoczyFWYbKMFk-image.png) [](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/tbLtAhKFBzhIDH3c-image.png) 根部分与Driver几乎一样,都使用了**Direct类型**的混合树,同时驱动多个混合树。我们以TongueOut为例,可以看到同样使用了**Direct类型**,所使用的参数为默认为1的参数,则同时进行三个**Motion**的混合。 [](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/eC5liLkymZQce2Tb-image.png) 而这三个嵌套的Blentree使用参数分别为FT/v2/TongueOut1、FT/v2/TongueOut2、FT/v2/TongueOut4,对应的动画命名均为 参数名+False/True+Postive/Negative+GUID [](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/jYMRndzuOb6oHVwj-image.png)[](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/NPlZHJeMoWW1ug51-image.png)[](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/JczvcXzZvAXCqd6P-image.png) [](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/57exIp1LrLN6PLTF-image.png) [](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/lLdDnSEP6op5BN7S-image.png)[](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/WEvVmyJxC0a9clHI-image.png) 这是一个特殊的关键帧,这是直接驱动动画器中对应的参数的关键帧,含义是将BinaryOut/FT/v2/TongueOut设置成0和0.28571,同时还有类似0.57143、0.14286,具体的取值不详细解释了,在上一页有讲到,是二进制压缩的原理内容。一般途径无法直接录制这个关键帧,通常在.anim文件中直接修改,或者使用代码生成,当这类关键帧启动的时候,动画器内被驱动的数值则会无法输入
Positive/Negative是区分类似MouthX这种有负值的参数使用的
当三个动画同时被驱动的时候,就能够将各自驱动的数值相加混合起来,也就是最开始这张图。 [](https://docs.vrcd.org.cn/uploads/images/gallery/2024-09/oYTY3KE7V9slqKDq-2024-09-19-23-29-06-1.gif) ### **3. Smooth图层内容详解** TBD