はじめに
uLipSync は Unity 上で音声データ / 音声入力をもとにリップシンクを実現するアセットです。
今回は要望を頂いていた以下の 2 つの機能追加・サンプル更新をしましたので解説します。
- 実行時にセットアップしたい
- Timeline でのリップシンククリップ配置を簡単にしたい
リリース
v2.6.0 をリリースしました。
実行時のセットアップ
例えば VRM をロードしてそこにコンポーネントをセットアップしたい場合、これまでは API が足りておらず自前で頑張る必要がありました。これを解決するために、いくつか API を生やし、それらを使うサンプルを 10. Runtime Setup に追加しました。あくまでサンプルなのでそのまま使える感じではありませんが...、また色々と要望を聞きつつアップデートしていこうと思います。
SkinnedMeshRenderer 経由で指定
方針としては、
uLipSyncBlendShape
をアタッチ- そこに
SkinneMeshRenderer
を登録 AddBlendShape(string phoneme, string blendShape)
でProfile
に登録された音素名と、それに対応するSkinnedMeshRenderer
の BlendShape を紐付けuLipSync
をアタッチProfile
を登録uLipSyncBlendShape
のコールバックを登録
となります。
using UnityEngine; using System.Collections.Generic; public class uLipSyncBlendShapeRuntimeSetupExample : MonoBehaviour { // リップシンクを追加したいゲームオブジェクト(AudioSource public GameObject target; // バインドしたいプロファイル public uLipSync.Profile profile; // ブレンドシェイプを含む SkinnedMeshRenderer public string skinnedMeshRendererName = "MTH_DEF"; // とりあえず何かしら適当なデータがあるとする [System.Serializable] public class PhonemeBlendShapeInfo { public string phoneme; public string blendShape; } public List<PhonemeBlendShapeInfo> phonemeBlendShapeTable = new List<PhonemeBlendShapeInfo>(); uLipSync.uLipSync _lipsync; uLipSync.uLipSyncBlendShape _blendShape; void Start() { if (!target) return; SetupBlendShpae(); SetupLipSync(); } void SetupBlendShpae() { // 指定した名前の GameObject を検索 var targetTform = uLipSync.Util.FindChildRecursively( target.transform, skinnedMeshRendererName); if (!targetTform) { Debug.LogWarning( $"There is no GameObject named \"{skinnedMeshRendererName}\""); return; } // SkinnedMeshRenderer を取得 var smr = targetTform.GetComponent<SkinnedMeshRenderer>(); if (!smr) { Debug.LogWarning( $"\"{skinnedMeshRendererName}\" does not have SkinnedMeshRenderer."); return; } // uLipSyncBlendShape をつける _blendShape = target.AddComponent<uLipSync.uLipSyncBlendShape>(); _blendShape.skinnedMeshRenderer = smr; // 何かしらのデータを使って Phoneme とブレンドシェイプ名をバインド foreach (var info in phonemeBlendShapeTable) { _blendShape.AddBlendShape(info.phoneme, info.blendShape); } } void SetupLipSync() { if (!_blendShape) return; // uLipSync をつける _lipsync = target.AddComponent<uLipSync.uLipSync>(); // プロファイルを指定 _lipsync.profile = profile; // uLipSyncBlendShape のコールバックを登録 _lipsync.onLipSyncUpdate.AddListener(_blendShape.OnLipSyncUpdate); } }
実際はユーザに任せる段では SkinnedMeshRendere
や BlendShape のリストを提供する UI などを用意する必要があるかもしれません。その際、音素名のリストは Profile.GetPhonemeNames()
から取得することが出来ます。
VRM の場合
VRM の場合は少し簡単で、VRM に登録されている情報を指定するだけで良いです。例えば VRM 0.x 向け uLipSyncBlendShapeVRM
コンポーネントを使う場合は以下のようにします。
public class uLipSyncBlendShapeVRMRuntimeSetupExample : MonoBehaviour { ... // VRM 用のスクリプト uLipSync.uLipSyncBlendShapeVRM _blendShape; ... void SetupBlendShpae() { _blendShape = target.AddComponent<uLipSync.uLipSyncBlendShapeVRM>(); // 音素と VRM のブレンドシェイプ名をバインド foreach (var info in phonemeBlendShapeTable) { _blendShape.AddBlendShape(info.phoneme, info.blendShapeClip); } } ... }
VRM 1.0 向けに uLipSyncExpressionVRM
を使って Expression を利用する場合も、同様に AddBlendShape()
すれば良いです(将来的に API 名は変更になるかもしれません…)。
所感
SkinnedMeshRenderer
直接利用の場合は、対応する BlendShape を検索・セットアップする手順が面倒そうですね。どれが口のどの形に対応するかはモデルごとにまちまちですし、特にルールが有るわけでもありません。名前自体も blendShape1.MTH_A
のように長くて分かりづらいです。
VRM の方は簡単で、VRM 0.x の場合は VRM.BlendShapeAvatar
で定義された VRM.BlendShapeClip
が A
や I
といったシンプルな名前を持っているのでよりユーザフレンドリーです。VRM 1.0 では BlendShape が Expression という呼称に代わっており、aa
、ih
といった国際標準名で与える形になります。また、BlendShape でなくジョイントを動かしたり UV を動かしたりする場合でも対応しているのでより汎用的です。コードも短くなりますし良いですね。
タイムライン上への自動クリップ追加
Audio Track に対応する形で uLipSync Track を自動追加したい、という要望を頂きましたので作ってみました。
uLipSync
Windows > uLipSync > Timeline Setup Helper でウィンドウを開きます。
このようなウィンドウが表示されます。
Timeline となっているフィールドに Timeline アセットを指定すると、そこに含まれる Audio Track および uLipSync Track がそれぞれプルダウンリストから選択できるようになります。追加していない場合は適宜追加してください。
Profile には BakedData
を自動生成する際に利用する Profile
(どの声でキャリブレーションを行うかのデータ)を指定します。Use Exiting Asset をチェックしている場合は、対象の AudioClip
を利用した BakedData
が存在した場合、新規生成せずにそれを利用します。Output Directory には新規生成した BakedData
を保存するディレクトリを指定してください。
これで次のように Timeline が自動セットアップされるようになります。
おわりに
ランタイムセットアップの方に関しては、あとは Profile
のランタイムでの構築、保存、読込のサンプルを用意しようと思います。
今回のように生の利用者の声がいただけると、今後も色々と改善に繋げられると思いますので、ぜひお気軽に Twitter などでお声がけください。