ARKitによるフェイストラッキングをやってみた

# はじめに
UnityからビルドしたiOSアプリでARKitを使って、フェイストラッキングをしました
参考記事で実装しているものを参考にさせていただいております！
今回はLive2Dを使用して自分の顔とアバターの表情を同期します！
一部省略しておりますのでご了承ください

※完成動画
| まばたき | 口パク | 顔の向き |
| ---- | ---- | ---- |
|![30254e211064d275bbc22a736fb6554b.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608328/50440a2a-40f6-56da-d5b9-14ce6ea9277f.gif)|![03065ee58c77fc78f4436148a832080e.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608328/beaa77c1-0082-f7a0-390a-3f8ad3ef23b4.gif)|![0b0448c3eda50e152ce9adeb9e63f8cc.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608328/b3f5d9ee-b20b-3623-18b7-6c0ebfd8f08b.gif)|

# アバターのインポート
Live2D Cubism公式から使用するアバターをインポートします
![スクリーンショット 2022-11-23 18.41.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608328/6b6e83fe-a731-dde4-efbd-1a8063671f6e.png)

# ARKitインポート
Package ManagerのUnity RegistryからAR Foundation、ARKit XR Plugin、ARKit Face Trackingの3つをインポートします
![スクリーンショット 2022-11-22 18.22.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608328/254ab9e7-6690-af1b-7d6a-144a116b2bbb.png)

Project SettingのXR Plug-in ManagementでARKitにチェックマークをいれます
![スクリーンショット 2022-11-22 18.50.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608328/54ffe5f7-7bb1-e896-f1a2-bcc9245d08ac.png)

同じくProject SettingのXR Plug-in ManagementのARKitでFace Trackingにもチェックマークをいれます
![スクリーンショット 2022-11-22 18.50.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608328/bda2c390-e603-2be8-65e2-25429de38657.png)

# 表情の同期
Sceneを作成して、XRからAR Session OriginとAR Sessionのオブジェクトを生成します
![スクリーンショット 2022-11-23 18.57.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608328/effcc36f-024f-4f54-4a72-502eeed720c1.png)

AR Session OriginにARFaceManagerのコンポーネントをアタッチします
子のAR CameraのAR Camera ManagerのFacing Direcctionを自分を写すようにUserにしておきましょう

今回自分の表情は画面に表示したくないので、Canvasを生成してAR Cameraをセットします
Canvasの子にImageオブジェクトを生成しましょう

また、表示したアバターを写すために別のCameraを生成します
下の画像の通り分かりやすいように少し位置をずらしています
![スクリーンショット 2022-11-23 19.06.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608328/8ab830ef-1679-87b8-adf2-e6329a7f73e7.png)

アバターに表情を同期します
今回一部のみ同期するようにしています
Prametersオブジェクトの子から変更するオブジェクトを取得してます
上から0,1,2,3...といった感じです

```
var cubismModel = avatar.FindCubismModel();
faceAngleX = cubismModel.Parameters[0];
faceAngleY = cubismModel.Parameters[1];
faceAngleZ = cubismModel.Parameters[2];
leftEye = cubismModel.Parameters[3];
rightEye = cubismModel.Parameters[5];
mouthForm = cubismModel.Parameters[19];
mouthOpen = cubismModel.Parameters[20];
```
![スクリーンショット 2022-11-23 19.18.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608328/7833f263-fee9-7e36-e0bd-ad2dcbbb1338.png)

表情が変わったら反映する処理を呼びます
```
private void OnEnable()
{
    faceManager.facesChanged += OnFaceChanged;
}

private void OnDisable()
{
    faceManager.facesChanged -= OnFaceChanged;
}

private void OnFaceChanged(ARFacesChangedEventArgs eventArgs)
{
    if (eventArgs.updated.Count != 0)
    {
        var arFace = eventArgs.updated[0];
        if (arFace.trackingState == TrackingState.Tracking
            && (ARSession.state > ARSessionState.Ready))
        {
            UpdateAvatarPosition(arFace);
            UpdateBlendShape(arFace);
        }
    }
}
```
最後に、ここでアバターの持つパラメーターと調整して値を計算して
アバターに反映します
```
private void UpdateBlendShape(ARFace arFace)
{
    using var blendShapesARKit = faceSubsystem.GetBlendShapeCoefficients(arFace.trackableId, Allocator.Temp);

    foreach (var featureCoefficient in blendShapesARKit)
    {
        if (featureCoefficient.blendShapeLocation == ARKitBlendShapeLocation.EyeBlinkLeft)
        {
            updateLeftEye = 1 - featureCoefficient.coefficient;
        }
        if (featureCoefficient.blendShapeLocation == ARKitBlendShapeLocation.EyeBlinkRight)
        {
            updateRightEye = 1 - featureCoefficient.coefficient;
        }
        if (featureCoefficient.blendShapeLocation == ARKitBlendShapeLocation.MouthFunnel)
        {
            updateMouthForm = 1 - featureCoefficient.coefficient * 2;
        }
        if (featureCoefficient.blendShapeLocation == ARKitBlendShapeLocation.JawOpen)
        {
            updateMouthOpen = (float)(featureCoefficient.coefficient * 1.8);
        }
    }
}
```
# 感想
参考記事の方の実装が分かりやすくスムーズに実装することができました
使用するアバターが2Dと3Dで反映する方法が多少違っているため調整が必要でした
また今回は最低限のパラメーターを取得して反映しているため、もっと細かく調整すれば
より滑らかな動きをさせることができると思います。

# 参考記事
https://qiita.com/amachi0/items/d830bd89a18b78745b84
https://www.live2d.com/download/sample-data/