LoginSignup
13

More than 1 year has passed since last update.

Unity AR Foundation の FaceTrackingを使ってバ美肉してみる

Last updated at Posted at 2021-12-18

Applibot Advent Calendar 2021」 19日目の記事になります。

前日は 「2021 年の Odin Inspector 事情 」という記事でした、Odinおしゃれだぁ

:movie_camera: できたもの

ブレイドエクスロードに登場するキャラクター、アミナ・ガーベルのモデルを利用し、FaceTrackingで表情を変化させてみました。

trim3.gif

※ 2021年の中頃まで弊社で運営、その後、株式会社マイネット様に運営移管しております。

※ マイネット様より、掲載の許可は頂いております。

なぜやったのか

当時(2020年頭)、リモートワークが増えてzoomでのMTGが増えた。当時ちょうど3D周りを少し触っていた。そして単純にVTuberが好きだった。

やってみる

環境

version
Unity 2019.3.12f1
PackageManager Unity AR Foundation 4.1.0
ARCore XR Plugin 4.1.0
ARKit Face Tracking 4.1.0
ARKit XR Plugin 4.1.0

Unity AR Foundation

Unity公式のAR開発用のフレームワーク、PackageManagerからインストールできる。

今回はFaceTrackingのみしか使いませんが、いろんな事ができます。

- 平面検知(Plane Detection)
- 画像追跡(Image Tracking)
- 物体追跡(Object Tracking)
- 表情追跡(Face Tracking)
- etc …

こちらの Unity-TechnologiesさんのGitHubにいろんなサンプルがあります。

Android/iOSともに利用可能、FaceTrackingに関してはiPhone X以降からの、FaceID搭載の端末であればより高精度に取れるっぽい?

1. サンプルシーンを実機で動かす

先程のGutHubからサンプルシーンを持ってきて、プラットフォームをiOSにしてXCodeプロジェクトを書き出し->実機でFaceTrackingができていることを確認してみる。

こうなる

FaceTrackingのシーンまで遷移するとこうなるはず。ナマケモノのモデルが自身の顔の上に表示され、顔の向きや口、瞬きなどがトレースされています。

2.gif

アタッチされているコンポーネントの処理を覗いてみると、SlothHeadプレハブについているARFace コンポーネントで顔の位置を同期して、ARKitBlendShapeVisualizerで表情のキーとパラメータをBlendShapeの値に流してるらしい、なるほど。

ARKitBlendShapeVisualizer.cs

// ARKitBlendShapeLocation と ブレンドシェイプの名前を紐付けている
void CreateFeatureBlendMapping()
{
    if (skinnedMeshRenderer == null || skinnedMeshRenderer.sharedMesh == null)
    {
        return;
    }

#if UNITY_IOS && !UNITY_EDITOR
    const string strPrefix = "blendShape2.";
    m_FaceArkitBlendShapeIndexMap = new Dictionary<ARKitBlendShapeLocation, int>();

    m_FaceArkitBlendShapeIndexMap[ARKitBlendShapeLocation.BrowDownLeft        ]   = skinnedMeshRenderer.sharedMesh.GetBlendShapeIndex(strPrefix + "browDown_L");
    m_FaceArkitBlendShapeIndexMap[ARKitBlendShapeLocation.BrowDownRight       ]   = skinnedMeshRenderer.sharedMesh.GetBlendShapeIndex(strPrefix + "browDown_R");

    // ~ 省略 ~

    m_FaceArkitBlendShapeIndexMap[ARKitBlendShapeLocation.TongueOut           ]   = skinnedMeshRenderer.sharedMesh.GetBlendShapeIndex(strPrefix + "tongueOut");
#endif
}
ARKitBlendShapeVisualizer.cs
// 紐付け情報をもとにブレンドシェイプの値を更新している
void UpdateFaceFeatures()
{
    if (skinnedMeshRenderer == null || !skinnedMeshRenderer.enabled || skinnedMeshRenderer.sharedMesh == null)
    {
        return;
    }

#if UNITY_IOS && !UNITY_EDITOR
    using (var blendShapes = m_ARKitFaceSubsystem.GetBlendShapeCoefficients(m_Face.trackableId, Allocator.Temp))
    {
        foreach (var featureCoefficient in blendShapes)
        {
            int mappedBlendShapeIndex;
            if (m_FaceArkitBlendShapeIndexMap.TryGetValue(featureCoefficient.blendShapeLocation, out mappedBlendShapeIndex))
            {
                if (mappedBlendShapeIndex >= 0)
                {
                    skinnedMeshRenderer.SetBlendShapeWeight(mappedBlendShapeIndex, featureCoefficient.coefficient * coefficientScale);
                }
            }
        }
    }
#endif
}

:beginner: BlendShape

CGWorldさんの記事がわかりやすいです。

Unityでは SkinnedMeshRenderer のインスペクター上に表示されています。今回のFaceTrackingではこの値を調整することで表情を変化させています。

skin.png

2. サンプルシーンのモデルを置き換えてBlendShapeを反映してみる。

ひとまず、ナマケモノのモデルを、今回使うモデルに置き換えていく。

ARFaceARKitBlendShapeVisualizerをモデルにアタッチ、ARKitBlendShapeVisualizerSkinnedMeshRendererをアタッチする。

ARKitBlendShapeLocation と ブレンドシェイプの名前を紐付ける

BlendShapeの命名はモデルによって違うので、今回は中間の紐付けを行う ScriptableObjectを作って値を流し込んでみました。

ARKitBlendShapeVisualizer.cs
[SerializeField]
private ARFaceBlendShapeBindData _data = null;

void CreateFeatureBlendMapping()
{
    // キーとブレンドシェイプ名を流し込む
    foreach (var bindInfo in _data.BindInfos)
    {
        m_FaceArkitBlendShapeIndexMap[bindInfo.Key] = skinnedMeshRenderer.sharedMesh.GetBlendShapeIndex(_data.Prefix + bindInfo.Name);
    }
}
ARFaceBlendShapeBindData.cs
using UnityEngine;
using UnityEngine.XR.ARKit;

[CreateAssetMenu(menuName = "Data/ARFaceBlendShapeBindData", fileName = "dat_blend_shape_bind" )]
public class ARFaceBlendShapeBindData : ScriptableObject
{
    [System.Serializable]
    public class BindInfo
    {
        public ARKitBlendShapeLocation Key;
        public string Name;
    }

    public string Prefix;
    public BindInfo[] BindInfos;
}

体の動き

ナマケモノは顔だけでしたが、アミナには身体があるので、顔~首と、体の部分のゲームオブジェクトを分離させ、体の方のゲームオブジェクトを首の根元を起点に顔の逆方向に回転させてみた。
ARFaceBodyARFaceと同じオブジェクトにアタッチ、その子階層に体のTransformを含めている状態。

ARFaceBody.cs
using UnityEngine;
using UnityEngine.XR.ARFoundation.Samples;

using UnityEngine;

public class ARFaceBody : MonoBehaviour
{
    [SerializeField] private Transform _body = null;

    void Update()
    {
        if (_body != null) {
            // オブジェクトの階層構造に寄ってはうまくいません
            _body.localRotation = transform.localRotation;
        }
    }
}

ついでにグリーンバックにする

背景がカメラの映像そのまま出るようになっているので、メインカメラについているAR Camera BackgroundUse Custom Material にチェックを入れて、緑一色のマテリアルをアタッチする。

:movie_camera: こうなった

face.gif

3. 目、揺れものを対応していく

まずは目

ナマケモノの目は、瞬きはしているものの、眼球自体は動いていませんでしたが、ARFaceには rightEye leftEye のプロパティが公開されているので、 ARFaceEyeコンポーネントを作り、毎フレームモデルの目のトランスフォームに同期させることで眼球を回転させることができました。

ARFaceEye.cs
using UnityEngine;
using UnityEngine.XR.ARFoundation;

public class ARFaceEye : MonoBehaviour
{
    [SerializeField] private ARFace _face;
    [SerializeField] private Transform _right;
    [SerializeField] private Transform _left;

    void Update()
    {
        if (_face.rightEye != null) {
            var angle = _face.rightEye.transform.localRotation.eulerAngles;
            _right.transform.localRotation = Quaternion.Euler( angle.x, angle.y, angle.z);
        }

        if (_face.leftEye != null) {
            var angle = _face.leftEye.transform.localRotation.eulerAngles;
            _left.transform.localRotation = Quaternion.Euler( angle.x, angle.y, angle.z);
        }

    }
}

揺れもの

Dynamic Boneを購入して各所に設定した。

Dynamic Boneの設定方法に関してはこちらが非常にわかりやすかったです。

一応、髪の毛のパラメータはこんな感じです。

param.png

:movie_camera: こうなった

comp.gif

完成!

おまけ

株式会社miHoYo様より公式で配布されている原神のキャラクターのMMDをMMD4Mecanimを介して同じことをしてみた。いざ使おうとするといろんなところで引っかかるので困ったら最近投稿されたこちらの記事がおすすめ。

mhy.gif

基本的な作りは一緒なので、ARFaceBlendShapeBindDataでマッピング情報を変えるだけで動いたりします。

終わり

参考リンクまとめ

※この記事の内容は個人の意見であり、所属団体の意見ではありません。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13