LoginSignup
0
0

【FaceMash】vrm使ってVtuberになれる仕組みを作ってみた(顔編)

Posted at

前書き

MediaPipeを勉強がてらVrmの表情操作を行えるようにしてみようと思って作りました
顔編と題しているものの、体編など作るかは未定です。。。(期待してたかたすいませんmm
また、コードの全文は記載しません
FaceMeshの頂点データの番号は記載します(調べるの面倒だったので少しでも助けになれば

 

完成品

tes.gif

右上が自分の顔の動きです
ちなみに、細かい部分の動きはかなり誤魔化してます

使用技術

・Unity2022.3.20f1
・vrm1.0
https://github.com/homuler/MediaPipeUnityPlugin
・UniTask

苦戦したこと

・頂点のIndex調べる
・vrmのパラメーターにfaceMeshの値を補正する
正直このぐらいでした
それ以外はすんなりできた

参考資料

↓の記事を参考に補正を作ってみたり
https://note.com/reality_eng/n/ndaddbaf3aaae
https://note.com/reality_eng/n/nb36f85b8123e

↓の記事から顔の向きを作ってみたり
https://zenn.dev/ykesamaru/articles/025b0eabf24e20

コード

頂点のIndex(2024/04/27時点

public static class FaceLandmarkIndex
{
    // 鼻の頭
    public const int NoseTop = 1;
    
    // 唇
    public const int UpperLipBottom = 13;
    public const int LowerLipTop = 14;
    public const int UpperLipTop = 0;
    public const int LowerLipBottom = 17;
    
    //口角
    public const int MouthCornerRight = 61;
    public const int MouthCornerLeft = 291;

    //輪郭
    public const int ContourBottom = 152;
    public const int ContourRight = 93;
    public const int ContourLeft = 366;
    public const int ContourTop = 10;

    //まぶた
    public const int LeftEyelidLower = 374;
    public const int LeftEyelidUpper = 386;
    public const int RightEyelidUpper = 159;
    public const int RightEyelidLower = 144;

    //眉
    public const int LeftBrowCenterUpper = 336;
    public const int LeftBrowCenterLower = 285;
    public const int LeftBrowOuterUpper = 293;
    public const int LeftBrowOuterLower = 283;
    public const int RightBrowCenterUpper = 107;
    public const int RightBrowCenterLower = 55;
    public const int RightBrowOuterUpper = 63;
    public const int RightBrowOuterLower = 53;

    //目
    public const int RightEye = 469;
    public const int LeftEye = 474;
}

全体のコード

public class VrmFace : MonoBehaviour
{
    [SerializeField] private Vrm10Instance vrmInstance;
    
    /* 補正値などを入れておくと便利 */
    
    private Vrm10RuntimeExpression vrm10RuntimeExpression;

    //
    private bool isInitialized = false;
    private bool isDestroyed = false;
    private NormalizedLandmarkList defaultFaceLandmarks;
    private NormalizedLandmarkList tempFaceLandmarks;
    
    private Quaternion neckDefaultRotation;
    private Quaternion chestDefaultRotation;
    
    
    private void Start()
    {
        //初期化処理を登録
        initializeButton.onClick.AddListener(InitializeFace);
        
        //VRMの表情を取得
        vrm10RuntimeExpression = vrmInstance.Runtime.Expression;
        
        //初期化
        /* 補正値 */
    }

    private void OnDestroy()
    {
        isDestroyed = true;
    }

    public void InitializeFace()
    {
        //デフォルトの表情を取得
        defaultFaceLandmarks = tempFaceLandmarks.Clone();
        isInitialized = true;
    }

    //自分でカスタマイズしたGraphRunnerとImageSourceSolutionから呼び出している
    public async UniTaskVoid SyncFace(NormalizedLandmarkList resultFaceLandmarksWithIris)
    {
        tempFaceLandmarks = resultFaceLandmarksWithIris;
        if (!isInitialized) return;

        // mediaPipe側でコールバックで呼び出される際はMainThreadにいない時があるため戻しておく
        await UniTask.SwitchToMainThread();
        if(this == null || isDestroyed) return;
        
        //表情の変更処理
        if (tempFaceLandmarks?.Landmark == null) return;
        
        var lipLength = GetPureFaceLandmarkDistance(FaceLandmarkIndex.UpperLipBottom, FaceLandmarkIndex.LowerLipTop);
        var mouseWidth = GetPureFaceLandmarkDistance(FaceLandmarkIndex.MouthCornerRight, FaceLandmarkIndex.MouthCornerLeft);
        var leftEyelidLength = GetPureFaceLandmarkDistance(FaceLandmarkIndex.LeftEyelidLower, FaceLandmarkIndex.LeftEyelidUpper);
        var rightEyelidLength = GetPureFaceLandmarkDistance(FaceLandmarkIndex.RightEyelidLower, FaceLandmarkIndex.RightEyelidUpper);
        
        /*非公開 口、口角、瞬きの処理*/
        
        /* ここから目の位置を変更する処理
         むずいのでこのままだと動かないためコメントアウト(代用としてLookAtを使用する
        /*非公開 目のトラッキング、調整がむずいため断念*/
        */
        
        //顔の回転
        /*非公開*/
        
        //体の向き
        /*非公開*/
        
        
        
        debugText.text = $"Lip: {lipValue}\n" +
                        $"Mouth: {mouthValue}\n" +
                        $"Blink: {leftBlinkValue} {rightBlinkValue}\n" +
                        /*$"Eye: {leftEyeMovement} {rightEyeMovement}"*/
                        $"Angle: {angleY} {angleX}";
        
        
    }
    
    private (float, float) GetPureMovableVector(int index)
    {
        /*非公開*/
    }
    
    private float GetPureFaceLandmarkDistance(int index1, int index2)
    {
        /*非公開 各Landmarkの座標の距離などを計算する関数*/
    }
    
    private float GetValue(float current, float higher, float defaultValue)
    {
        /*非公開 瞬きや口の補正値関数*/
    }
}


0
0
0

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
0
0