パーフェクトシンク対応(VRM1)

Blender で パーフェクトシンク用のシェイプキー(Shape Key)を追加して、Unity で VRM1 のブレンドシェイプに設定する手順について説明する。VRM0 に関しては、「パーフェクトシンク対応(VRM0)」という記事を参照してほしい。

前提

VRoid で作成したモデルは想定していない。

パーフェクトシンクとは

パーフェクトシンクは、ARKit[1] で取得出来る BlendShapeLocation[2] 全52点を VRM の同名 BlendShapeClip に適用させる設定です。(中略)
パーフェクトシンクを使う事で、実際のユーザーの表情を直接アバターに適用する事が出来ます。これによって、非常に表情豊かな表現をすることが出来ます。(中略)
「パーフェクトシンク」はARKitの特徴を利用してモーフィングする挙動を指す造語です

パーフェクトシンクについて
  1. Apple 社が開発した iOS デバイスで動作する AR フレームワーク、API
  2. 検出された表情における顔の部位の位置(動き)を係数として記述したもの

スクリプト①

import bpy
def add_shape_keys():
    
    keys = [
        "Basis",
        "BrowDownLeft",
        "BrowDownRight",
        "BrowInnerUp",
        "BrowOuterUpLeft",
        "BrowOuterUpRight",
        "CheekPuff",
        "CheekSquintLeft",
        "CheekSquintRight",
        "EyeBlinkLeft",
        "EyeBlinkRight",
        "EyeLookDownLeft",
        "EyeLookDownRight",
        "EyeLookInLeft",
        "EyeLookInRight",
        "EyeLookOutLeft",
        "EyeLookOutRight",
        "EyeLookUpLeft",
        "EyeLookUpRight",
        "EyeSquintLeft",
        "EyeSquintRight",
        "EyeWideLeft",
        "EyeWideRight",
        "JawForward",
        "JawLeft",
        "JawOpen",
        "JawRight",
        "MouthClose",
        "MouthDimpleLeft",
        "MouthDimpleRight",
        "MouthFrownLeft",
        "MouthFrownRight",
        "MouthFunnel",
        "MouthLeft",
        "MouthLowerDownLeft",
        "MouthLowerDownRight",
        "MouthPressLeft",
        "MouthPressRight",
        "MouthPucker",
        "MouthRight",
        "MouthRollLower",
        "MouthRollUpper",
        "MouthShrugLower",
        "MouthShrugUpper",
        "MouthSmileLeft",
        "MouthSmileRight",
        "MouthStretchLeft",
        "MouthStretchRight",
        "MouthUpperUpLeft",
        "MouthUpperUpRight",
        "NoseSneerLeft",
        "NoseSneerRight",
        "TongueOut"
    ]
    if bpy.context.mode != 'OBJECT':
        bpy.ops.object.mode_set(mode='OBJECT')
    
    for key in keys:
        bpy.context.active_object.shape_key_add(name=key)
    
add_shape_keys()

空のシェイプキーを追加する

  1. ワークスペースを Scripting に変更する
  2. シェイプキーを追加したいオブジェクトを選択する
  3. 上記のスクリプト①をコピペする
  4. スクリプトを実行する

VRChat や VRM で使用する空のシェイプキーも追加する場合は、「VRChat と VRM で使用する空のシェイプキーを追加する」という記事を参考にしてほしい。

表情を作成する

はいぬっかさんの以下の記事を参考にして、Blender で表情を作成する。
パーフェクトシンク対応モデルをつくろう! iPhone顔トラッキング用 52BlendShapes制作メモ – はいぬっかメモ (hatenablog.com)

スクリプト②

using System.IO;
using UnityEditor;
using UnityEngine;

namespace UniVRM10
{
    public class AddPerfectSyncExpression : MonoBehaviour
    {
        // AssetsフォルダのVRMプレハブをセットしてもらう
        public GameObject vrmAsset;
        
        // 縦の三点リーダーから関数を実行してもらう
        [ContextMenu("AddPerfectSyncExpression")]
        public void AddBlendShapeClip()
        {
            // パスを特定する
            var vrmAssetPath = AssetDatabase.GetAssetPath(vrmAsset);
            var vrmAssetName = vrmAsset.name + ".vrm";
            var clipAssetFolderName = vrmAsset.name + ".vrm1.Assets";
            var clipAssetFolderPath = vrmAssetPath.Replace(vrmAssetName, clipAssetFolderName);
            if (!Directory.Exists(clipAssetFolderPath)){
                Directory.CreateDirectory(clipAssetFolderPath);
            }

            // vrmのinstanceを変数にセットする
            var instance = AssetDatabase.LoadAssetAtPath<Vrm10Instance>(vrmAssetPath);

            // BlendShapeがあるmeshを特定する
            Mesh mesh = null;
            var meshName = "";
            int blendShapeCount = 0;
            foreach (var transform in instance.transform.GetChildren())
            {
                var skinnedMeshRenderer = transform.GetComponent<SkinnedMeshRenderer>();
                if (skinnedMeshRenderer != null)
                {
                    blendShapeCount = skinnedMeshRenderer.sharedMesh.blendShapeCount;
                    if (blendShapeCount != 0)
                    {
                        mesh = skinnedMeshRenderer.sharedMesh;
                        meshName = mesh.name.Replace(".baked", ""); // Replace必要?
                        break;
                    }
                }
            }

            // clipのassetを作成して、instanceに追加する
            for (int i = 0; i < blendShapeCount; i++) {
                name = mesh.GetBlendShapeName(i);
                var clipPath = clipAssetFolderPath + "/" + name + ".asset";
                if (!File.Exists(clipPath))
                {
                    var key = ExpressionKey.CreateCustom(name);
                    var clip = ScriptableObject.CreateInstance<VRM10Expression>();
                    clip.Prefab = vrmAsset;
                    clip.IsBinary = false;
                    clip.name = name;
                    AssetDatabase.CreateAsset(clip, clipPath);
                    instance.Vrm.Expression.AddClip(key.Preset, clip);
                }
            }

            // blendShapeと同名のpresetのclipがあれば、weightを設定する
            foreach (var clip in instance.Vrm.Expression.Clips)
            {
                var idx = mesh.GetBlendShapeIndex(clip.Clip.name);
                if (idx != -1)
                {
                    clip.Clip.Prefab = vrmAsset;
                    var MTB = new MorphTargetBinding
                    {
                        Index = idx,
                        RelativePath = meshName,
                        Weight = 1.0f
                    };
                    clip.Clip.MorphTargetBindings = null;
                    clip.Clip.MorphTargetBindings = new MorphTargetBinding[] { MTB };
                }
            }
        }
    }
}

Expression をスクリプトで設定する

Unity にて、3D モデルを Export VRM した後、以下の手順を実施する。

  1. Assets フォルダの適当な場所に、AddPerfectSyncExpression という名前の C# Script を作成する
    (Project タブで右クリック > Create > C# Script)
  2. スクリプトをダブルクリックしてエディタで開き、上記のスクリプトをコピペして上書き保存する
  3. GameObject を新規に作成する(Hierarchy タブで右クリック > Create Empty)
  4. GameObject にスクリプト②をアタッチする
  5. VRM のプレハブをスクリプトの変数にセットする(Project タブからドラッグ&ドロップ)
  6. 関数(AddPerfectSyncExpression)を実行する

「Extract Meta And Expressions …」をクリックする。

_vrm1_ の inspector にて、Expression を表示して、Clip が追加されたことを確認する。

最後に、作成したスクリプトとゲームオブジェクトを削除する。

ドキュメント

Apple Developer – Arkit / ARFaceanchor / BlendShapeLocationARKit
Unity Documentation – Enum ARKitBlendShapeLocation | ARKit Face Tracking | 1.0.14 (unity3d.com)

所感

  • VRoid で作成したモデルは、頂点データが同じであるため、自動でパーフェクトシンクを設定してくれるツールが存在する。フルスクラッチで作成したモデルは、52 個の表情を手動で作成するしかない。骨の折れる作業だ。
  • スクリプトには必要最低限のコードしか書いていないので、適宜修正して使ってほしい。
  • パーフェクトシンクという名前は大げさだと思う。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です