チュートリアル / Bifrost for Maya Rigging Challenge~一歩先のリグ・アニメーションに挑戦~
第4回:速く動かすと変形するリグ
- Maya
- アニメ
- キャラクター・リグ
- ゲーム
- コラム
- シミュレーション
- チュートリアル
- 上級者
- 中級者
- 映画・TV
みなさん、こんにちは。
本コラムではMayaのプラグイン"Bifrost"を使って、リグ、アニメーション、物理シミュレーションなどの観点から作成例を紹介していきます。今回は「ある一定速度を上回ると変形を伴うリグ」を作ってみたいと思います。残像・モーションブラーのようなものをリグ制御で表現してみようという試みです。
成果物は以下のGIFの通りです。こちらを作っていきます。
【環境】
・Windows 11
・Maya 2025.3
・Bifrost 2.11.0.0
状況の確認
まずはこちらをご確認ください。剣のモデルにチェーン状のジョイントが配置してあり、スキニングしてあります。この剣を“素早く振った時にのみ”変形やトレイルの効果が欲しいものとします。
剣モデルを曲げる必要がありますが、これはすでに入っているジョイントをなんとかして回転させれば良さそうです。振った方向に少しだけ剣幅が広がる要素も欲しいですが、同じくジョイントのスケール操作を試みます。また、尾を引くようなエフェクトが出ていますが、これもメッシュのデフォームで統一してしまおうと思います。そして何より重要なのは、今回は“動きがトリガーになる”ということです。ある1フレームの瞬間における情報だけではなく、前後時間の状態を参照する必要があります。
それでははじめていきます!
直前の状態を保存
まず初めに今回の重要な要素としてフィードバックポートという機能を紹介しておきます。フィードバックポートは一言で言うと“前回の評価(*1)結果をキャッシュし次回再利用する”機能になります。主にシミュレーション系の実装で活躍しますが、できることとしては「値を保存する」というシンプルなものですので単にキャッシュとしての用途にも使えます。
次のグラフでは、ロケーターの位置をグラフへ入力し位置をpoint_scopeで表示しています。入力そのままの値を赤色のポイント、フィードバックポートに保存した前回の値を緑色のポイントで表示しています。ロケーターにキーを打ってタイムスライダを進める(*2)と、直前(1フレーム前)にロケーターがいた位置が緑色のポイントで表示されていることがわかります。
*1) Mayaにおける「評価」とは、シーン内のノードの状態や関係を更新し、正しい位置、回転、スケールなどを反映させる工程を指します。アニメーション再生中はフレームが進むごとに評価が行われます。
*2) マニピュレーターを動かした場合でも直前の位置は表示されます。ただし、マニピュレーター操作時はアニメーション再生時と違って更新頻度が高くなるため“直前の位置”は再生時と比べて現在位置にかなり近くなると思います。
緩やかに追従させる
緑の点(以降は制御点Pと呼びます)はほんの一瞬前の状態を表示しているだけですので、ロケーター(以降はターゲットTと呼びます)が停止した直後すぐにTに重なってしまいます。これをもっと緩やかにTに近づいてくるように変更したいと思います。やり方は非常に簡単で、PとTを一定の割合αで線形補間するだけです。得られた結果P’は同じくフィードバックポートに繋ぎ次回Pとして再利用します。
図で示すと以下のとおりです。1ステップ更新するごとにPは少しだけTに近づくようになります。また、1ステップあたりのPの移動量はP-T間の距離が長いほど大きくなります。
Tは常時動いていても問題ありません。Pはその都度Tに向かって少し移動します。lerpのfractionに繋いだα値は0.0〜1.0の間で自由に設定することが出来ます。0でまったく追従しなくなり、1だと逆に遅延がなくなりTにぴったり重なります。
これをうまいこと制御できれば、残像的なものが作れそうな気がしてきました!
一定速度を超えたら...
前項に加えて、Tが一定速度を超えた時にのみPが遅れてくるような条件を追加してみます。瞬間速度は位置の変化量を経過時間で割ると得られますが、ここでは正確な速度値を必要としているわけでは無いので単純に変化量だけを見ることにします。
以下のグラフはTの位置の変化量が一定の値を超えたらlerpのfractionを下げていき、変化量が最小のときfractionが1.0、最大になると0.2になるように設定したものです。位置の変化量は今の位置と前の位置の差ですので、ここでもフィードバックポートが活きてきます。
位置の代わりに角度で制御する
前項までのグラフは全て位置位置での制御でした。位置の変化量をトリガーとして位置を線形補間しています。ただ、冒頭でお見せしたモデルはどちらかと言うと振り回す系ですので、回転運動で制御したいです。そこで位置を角度に置き換えていきます。具体的には、ターゲットベクトルVtと制御ベクトルVpを取り扱い、Vtの向きの変化量をトリガーとして、Vpを角度で補間します。
以下は、Vtの変化量が一定の角度を超えた時のみ、Vpが遅れてついてくるようにしたグラフです。ベクトルを角度で操作するために、第2回で作成した「angle_between_vectors」や「rotate_vector_around_axis」を使用しています。
任意軸でのスケール
回転角の大きさに応じてジョイントをスケールしたいのでその仕組みを用意します。Z軸やY軸など固定軸でのスケールではなく動作の方向へスケールしたいため、任意の方向へのスケール操作を試みます。任意軸でスケールを行うとシアーを伴いますが、以下の計算式を使うとその「シアーを含む変換行列」を得られます。得られた行列Mは、分解してtransformノードやjointのRotate、Scale、Shearに適用します。
・R:入力ベクトルをローカル軸(例えばX軸)に一致させる回転行列
・R^−1:元の空間に戻すための逆行列
・S:スケール行列
以下のグラフは、指定したベクトルを軸にスケールし、結果を球体に適用したサンプルです。
これをチェーン状の各ジョイントに適用すると以下のような結果になります。子に行くにつれて二重三重にスケールが掛かるので、末端に行くほどスケール幅が大きくなります。Segment Scale Compensateはすべてオフにしてあります。
ここまでの結果を元にリグを完成させる
では、ここまでの要素をまとめて冒頭のモデルに適用していきます。
各ジョイントの親にtransformノードをひとつずつ追加して回転用ノードとし、曲げ要素としてVtとVpから取得した回転値を接続していきます。回転値が想定以上に大きくならないよう、念のため最後に角度制限を加えています。角度制限は第2回で作成した「angle_limit」を使用しています。
スケール軸に使うベクトルはVpをVtに投影した際の垂直成分を使用し、前項の「scale_along_any_vector」を通して得られたShear、Scale、Rotateを全ジョイントへ接続していきます。
グラフは以下のようになりました!
同様の仕組みでエフェクトモデルを追加
最後にもうひとつだけ、追加で下図のようなトレイルエフェクト用のモデルを作成しておきます。各ジョイントは独立していてモデルを簡単にスキンバインドしているだけです。これが本体の回転運動に合わせて刃先あたりから飛び出すようにしてみたいと思います。
本体に使用した仕組みと同様に、少し遅れた回転値を求めそれをトレイルモデルのジョイント数で均等に分割します。トレイルの開始地点を新規ロケーターで指定し、分割した各回転値を使用して柄の部分を基点にトランスフォームします。グラフは先ほどのものと別で新規作成したものを使用しています。
振る方向によって発生するかどうかを制限したいので、向きの制限も加えてあります。Vt(current)とVt(previous)の差のベクトルと、新たに定義した制限用ベクトルとの内積を求め、影響度として乗算します。
Vt(previous) - Vt(current)が水色の矢印、制限用ベクトルがピンクの矢印、最終的に得られたトランスフォームは transform_scope で表示しています。
これで完成です!
ちなみに、フィードバックポートはその特性上、フレームがジャンプすると結果が変わりますので、Playback speedは「Play every frame」にして確認する必要があります。また、レンダリング前にはアニメーションキーへベイクする必要があります。
今回はフィードバックポートを使って簡単な残像効果を作成してみました。アニメーターが作成するスミアフレームのような繊細さは無いかと思いますが、自動である程度の水準にはいける気がします!
Mayaの標準ノードだけでは評価結果を再利用する仕組みは作れませんので、Bifrostを上手く利用すれば出来ることの幅が増えることが伝わったかと思います。
また次回をお楽しみに!
今回のまとめ
・フィードバックポートは値を保存し次回再利用できる
・値を保存できると「動作を遅れさせる」「速度を計算する」「軌跡を得る」など、出来ることが増える
・回転行列とスケール行列を組み合わせて任意のベクトル方向へスケールできる