見出し画像

開発者ブログ Unityのマッスルについて

今回の開発者ブログを担当しますVKetCloudSDK開発部のプログラマーダンテと申します。

  1. Intro
    私は以前からVketCloudSDKで使われていたHEM Animation Converterの開発を担当しておりました。内部的な仕組みの全部は話しませんが、本日はHEM Animation Converterの開発を通じて得たノウハウを一般的なTipsに落とし込み、応用事例を交えつつお話しできればと思います。
    まず、HEMAnimationConverterとは、UnityのHumanoidアニメーションファイルをHEM(VketCloudが使用しているアニメーションフォーマット)に変換するものです。UnityのHumanoidアニメーションは後述のMuscle値と呼ばれる人体骨格に合わせて姿勢を表現しています。HEMでは、端的に言えば特定のボーンのパスを指定し、そのボーンの回転としてクォータニオンを使っている形式をとります。

  2. Humanoidアニメーションについて
    まず、Humanoidアニメーションは下図のような形式となっております。

図の通り、各ポーズの各軸回転について-1から1で値をとっています。これをMuscleといい、文字通りHumanoidモデルがどれだけ筋肉を動かしたかを示します。Muscle値の-1から1の範囲は、人の骨格の可動範囲内で回転の値をとるようになっています。たとえば、UpperArmL_FrontBackが0.8であれば、腕を60度ピッチングさせ、キャラモデルが腕を前に出すなどです(値は例とし適当に上げてるだけで実際のものではないです)。つまり、このMuscle値を使うHumanoidアニメーションは、人の制御するうえで非常に便利ということです。
一方で、HEMは各ボーンに対するクォータニオンの値がそのまま格納されています。そのため、HEM Animation Converterでは、Muscle値からクォータニオンに変換する必要がある、ということです。Legacyアニメーションへの変換方法は多々あると思いますが、今回はHumanoidのMuscle値を書き換えてそのボーンのクォータニオンを読み取る方法を選択しました。余談ですが、最初はMuscle値をそのままボーンの回転値に変換する数式を立てて、その値を取得する手法を取ろうとしていました。しかし、思うようにMuscle値が正解の回転値にすることができなかったため、断念しました。
そこで、読み取ったAnimationを1フレームずつ分解し、各フレームにおける全Muscle値をHumanPoseというものに格納します。HumanPoseとは、Humaoidキャラモデルのポーズを姿勢などを表すクラスのことです。HumanPoseには全ボーンのMuscle値を表すmusclesというメンバ変数が用意されております。このmusclesに値を入力することでHumanoidキャラに任意のポーズをリターゲットすることが可能です。つまり、スクリプトからアニメーションさせることが可能となります。

Muscle値を入れたときの姿勢変化

具体的なコードは下記のようになります。

HumanPoseHandler humanPoseHandler=new HumanPoseHandler(animator, animator.transform); 
HumanPose humanPose=new HumanPose();

var humanpose = new HumanPose(); 
humanPoseHandler.GetHumanPose(ref humanpose);

humanPose.muscle[i]=xxx//ここに特定のボーンに対して、Muscleの値をいれる
humanPoseHandler.SetHumanPose(ref humanpose);//humanPoseを反映させる

HEM Animation Converterでは、読み込んだHumanoid アニメーションファイルのMuscle値を1フレームずつHumanoidキャラモデルに読み込ませています。そうすると、アニメーションのnフレーム目の姿勢をとるので、その姿勢のときの各ボーンのクォータニオン値を取得し、保存することでHEM用のアニメーションに変換しています。これをnフレーム分、繰り返し行います。

3.応用事例

Muscle値を使った一般的なユースケースとしてはランタイムに生成したアニメーションを再生するのに役立つことがでしょうか。たとえば、最近はやりのChatGPTなどの大規模言語モデルは従来の使用用途である対話チャットボットとして優秀なだけでなく、既存の自然言語処理もやってくれます。たとえば、対話文章に対応した動作を文章として生成して追記するなどがあげられます。割と精度が高く生成してくれるので、Humanoidアニメーションに対応した姿勢に関するクリップもJson形式で吐き出すこともプロンプト次第では可能だと考えられます。これを利用して、LLMが吐き出したHumanPoseにMuscle値を格納してアニメーションを生成することが可能でしょう(もっとも、より抽象的な動作文章を追記してText2Motion用のクエリにするのが妥当だとは思います)。

Animation ClipはUnityEditor関数なので、ランタイムで吐き出すことはできません。ですが、キーフレームとMuscle値の束をJSonで持っておいて、フレームの順方向にMuscle値をセットしていけば疑似的にランタイムでアニメーションを作成・再生することはできます。ただし、ボーン分のイテレーションが毎フレーム挟まるのでフレームレートは保証できないのと、アニメーションブレンドは自作するしかありませんので工夫は必要です。



この記事が参加している募集