LoginSignup
8
9

More than 5 years have passed since last update.

Maya で毛糸玉に追従する糸を表現する方法

Posted at

はじめに

まずは以下の動画を御覧ください。

https://vimeo.com/259453255
https://vimeo.com/259453255

本稿では、この動画で使用されている糸の動きについて、実現するまでのプロセスを紹介します。なお、あくまで自分が試行錯誤して得た結果なので、もっと簡単な方法があるかもしれません。もし他の方法を知っている方がいましたら、コメントで教えてください。

サンプルシーンのダウンロード

仕組みをすっ飛ばしてシーンだけ見たいという方は、github にシーンを配置していますので、ダウンロードしてみてください。

利用するツール

  • Maya 2017 Update5

要件の整理

実現方法を検討する前に、要件を整理します。
糸を動きを作る上で、以下の要件を満たす必要があります。

  • 糸の柔らかさの表現
  • 毛糸玉の軌跡への追従
  • 他のモノ(机や椅子など)との衝突
  • 工数がかからない方法

あとで説明するように、実現するまでにいくつかの方法を試しています。これらの要件と照らし合わせながら最適解を探っています。

実現方法の検討

いくつかの方法を検討しました。

  • クラスタデフォーマを使う
  • 押し出しのパスカーブを使う
  • nHair を使う

クラスタデフォーマ(Cluster Deformer)を使う

方法

  • NURBS カーブの CV をクラスタ化にする
  • クラスタにアニメーションを手付けする

問題点

  • アニメーションは全て手付けになり工数がかかる

押し出し(Extrude)のパスカーブを使う

方法

  • メッシュに対して押し出し(Exclude)を適用する
  • 参考動画

https://www.youtube.com/watch?v=c-Zpamrvj6s

問題点

  • UV も一緒にアニメーションしてしまうため表現力に乏しい
  • アニメーションは手付けになるので工数がかかる

ヘアー(nHair)を使う

方法

  • NURBS カーブにヘアーを適用する
  • 糸のリアルな表現が工数をかけずに実現できる

問題点

  • 手付けできないので細かい手直しができない

試行錯誤の結果、ヘアーが求めている表現に近く、工数がかからないと判断したので、ヘアーを採用することにしました。しかし、いくつかの問題をクリアする必要がありました。

  • どのように糸を毛糸玉に追従させるのか
  • 地面に落ちていない糸はどこに隠すのか
  • NURBSカーブをどのようにレンダリングするのか

上に挙げた問題を1つずつ解決していきます。

実現方法

螺旋状のNURBSカーブの作成

ヘアーを使うとなったときに、一番悩ましいのが、まだ地面に落ちていない糸をどのように隠すかということでした。
糸を一箇所にまとめてしまうと、糸自身に衝突してしまい不具合の元になります。
そこで NURBS カーブをコイルのように螺旋状に巻いて、毛糸玉の中に収まるようにしました。

螺旋のNURBSカーブを作り方を説明します。
Helix のプリミティブを作成します。

Screen Shot 2018-03-05 at 9.06.25.png

Helix の外周のエッジを選択します。

Screen Shot 2018-03-05 at 9.06.46.png

Modify → Convert → Polygon Edges to Surface を適用します。

Screen Shot 2018-03-05 at 9.07.29.png

これで螺旋の NURBS カーブが作れます。

Screen Shot 2018-03-05 at 9.07.43.png

しかし、ここで重要なのは NURBS カーブの長さです。NURBS カーブの長さが糸の長さになるからです。そして NURBS カーブの長さを決めるのは、Helix のアトリビュートです。必要な糸の長さからアトリビュートに入力する数値を決めていきます。すごく雑に計算すると、以下の式になります。

糸の長さ \fallingdotseq 円周 \times 螺旋を巻いた回数 

糸の長さは 2m にします。毛糸玉のサイズは直径 10cm なので、螺旋はその中に隠れる必要があります。ということで直径 6cm 程度にします。螺旋を巻く回数は、

200 = 6 \times 3.14 \times x
x = 200 \div (6 \times 3.14)
x \fallingdotseq 10.615

となり、11回程度巻いておけば 2m になる計算です。Helix のアトリビュートを調整し、約2mのNURBSカーブを作ります。

Screen Shot 2018-03-11 at 13.28.06.png

Freeze Transform を適用して、ヒストリを切っておきます。

NURBSカーブのCV数の変更

次に考慮するのは、NURBS カーブのCVの数です。CVの数が多ければ、それだけ柔らかい糸の形状を表現できますが、今度は計算量が増えてしまいます。適切なCV数になるように修正します。今回は 1cm 毎にCVを入れることにします。
NURBSカーブを選択し、 メニューから Curves → Rebuild を選択します。

Screen Shot 2018-03-05 at 9.17.02.png

1cm 毎にCVを入れるので、オプション画面で Number of spans の値を 200 に変更して適用します。

Screen_Shot_2018-03-11_at_13_38_44.png

これでCV数が203(200+3)の NURBS カーブができます。
(なぜ3足されるのかよく分かってないです…すいません…)

NURBSカーブのヘアー化

螺旋状の NURBS カーブにダイナミクスを適用します。NURBSカーブを選択した状態で nHair → Make Selected Curves Dynamic を実行します。

Screen Shot 2018-03-11 at 13.42.52.png

初期状態ではカーブの両端が落下しないので、アトリビュートの内容を編集します。
Follicle AttributesPoint LockNo Attach に変更します。

Screen_Shot_2018-03-11_at_13_43_47.png

もう一つ、 nucleusSpace Scale を調整します。Maya の規定の作業単位はセンチメートルで、重力はメートル単位で解釈されるため、Space Scale0.01 にしておく必要があります。

Screen_Shot_2018-03-11_at_13_52_34.png

試しにアニメーションを再生してみます。

dynamics01.gif

当然ながら、糸はすぐに自由落下します。糸を毛糸玉に追従させるには、CVに対してダイナミクスを反映するかしないかを制御する必要があるのです。

Transform Constraint の適用

ダイナミクスのオン/オフを制御するために、 Transform Constraint を使用します。言葉だと説明が難しいので、Transform Constraint の効果の分かりやすい動画があるので紹介します。

https://www.youtube.com/watch?v=TwhTxlFaEO0
https://www.youtube.com/watch?v=TwhTxlFaEO0

以下の対応を行います。

  1. CV の親になるノードを作成する
  2. 出力カーブのCV毎に Transform Constraint を適用する
  3. Transform Constraint を親ノードに Parent Constraint させる
  4. 入力カーブを親ノードにParent Constraint させる

まず、親ノードを作成します。
空のグループを螺旋の中心に配置します。ノード名は YarnTrailParent としておきます。

Screen Shot 2018-03-11 at 14.15.15.png

次に、CV毎にコンストレイントを適用していくのですが、 203個もあるので手動で行うのは大変です。そこで、ここでは MEL を使います。
以下のスクリプトを Script Editor に貼り付けて実行します。親ノード(YarnTrailParent)を選択した状態で実行してください。


$select = `ls -sl`;
$parent = $select[0]; 

for($i = 0; $i <= 202; $i++)
{
    // create dynamic constraint
    selectKey -clear ;
    select -r curve1.cv[$i];
    createNConstraint transform 0;

    // create parent constraint
    $number = $i + 1;
    $constraint = "dynamicConstraint" + $number;
    select -cl;
    select -r $parent;
    select -add $constraint;
    doCreateParentConstraintArgList 1 { "0","0","0","0","0","0","0","0","1","","1" };
    parentConstraint -mo -weight 1;

    // disable rest position
    $parentConstraintName = $constraint + "_parentConstraint1";
    string $enableRestPositionName = $parentConstraintName + ".enableRestPosition";
    select -r $parentConstraintName;
    setAttr $enableRestPositionName 0;
}

実行すると、大量のコンストレイントがアウトライナ上に表示されます。

Screen Shot 2018-03-11 at 14.20.10.png

邪魔なのでグループにまとめて非表示にします。

Screen Shot 2018-03-11 at 14.21.15.png

最後に入力カーブに対して Parent Constraint をかけます。親ノード(YarnTrailParent)を選択してから、入力カーブを選択します。

Screen Shot 2018-03-11 at 15.27.32.png

Constrain → Parent Constraint を実行します。

Screen Shot 2018-03-11 at 15.29.14.png

この状態でアニメーションを実行すると、ダイナミクスは適用されず、糸は静止したままの状態になります。このコンストレイントのオン/オフを制御すれば、糸の軌跡を描くことができそうです。

糸制御用コントローラの作成

螺旋がダイナミクスの影響を受けず、親ノードに追従するようになりました。
次に糸制御用のコントローラを作成します。このコントローラには以下のパラメータを持たせます。

  • 現在の角度 (Angle)
  • 初期状態の角度 (Initial Angle
  • CV の個数 (Cv Count
  • CV 1つの角度 (Cv Angle
  • AngleInitial Angle を加算した角度 (Trail Angle)

コントローラの形状はなんでもいいのですが、螺旋を覆う感じで円を描きます。ノード名を YarnTrailCtrl とします。

Screen Shot 2018-03-11 at 15.53.02.png

Freeze Transform してヒストリを切ります。そして、親ノード(YarnTrailParent) をコントローラの階層に移動します。

Screen Shot 2018-03-11 at 15.56.32.png

これでコントローラの移動・回転が螺旋に反映されるようになります。
コントローラのアトリビュートを以下のように編集します。

Screen_Shot_2018-03-11_at_22_33_29.png

ScaleVisibility は使用しないので Lock and Hide Selected を適用します。追加分のアトリビュートは Add Attribute で追加します。追加したアトリビュートの名前は、後でスクリプトで使用するので厳密に決めておく必要があります。
まだ何も制御はできていませんが、これで糸制御用のコントローラができました。

糸の排出を制御する

球の回転に応じて、頂点毎のコンストレイントのオン/オフを制御します。球の回転角はフレーム毎に変わるので、フレーム単位で値を評価する必要があります。
そこで Expression を利用します。以下のような処理をフレーム毎に行うExpressionを記述します。

  1. コントローラの回転角(Angle)と初期角度(Initial Angle)を加算し現在の角度(Trail Angle)を取得する
  2. 回転角を単位角(Cv Angle)で除算する
  3. 除算で得た値の数から、対象となるCVを取得する
  4. CVの Transform Constraint 及び Parent Constraint を無効にする

Expression Editor を開きます。

Screen Shot 2018-03-11 at 22.44.57.png

Expression Name は適当に YarnTrailController とします。
以下のスクリプトを Expression Editor に貼り付けて、Create ボタンを押します。


$cvAngle = YarnTrailCtrl.CvAngle;

$cvCount = YarnTrailCtrl.CvCount;

$trailAngle = YarnTrailCtrl.Angle + YarnTrailCtrl.InitialAngle;

YarnTrailCtrl.TrailAngle = $trailAngle;

$count = $trailAngle / $cvAngle;

for($i = 0; $i < $cvCount; $i++)
{
    int $number = $i + 1;
    string $dynamicContraintShape = "dynamicConstraintShape" + $number;
    string $parentContraintWeight = "dynamicConstraint" + $number + "_parentConstraint1.YarnTrailParentW0";

    $enableConstraint = $i < $count ? 0 : 1;

    setAttr($dynamicContraintShape + ".enable", $enableConstraint);
    setAttr($parentContraintWeight, $enableConstraint);
}

エラーが発生したときは、コントローラのアトリビュートの命名が間違っている可能性があります。
次にコントローラにパラメータを入力します。CV Count には CV の総数を入れるので 203CV Angleには19.5を入力します。

Screen Shot 2018-03-11 at 17.02.02.png

CV Angle19.5 という値は、以下の大雑把な計算で求めています。厳密な値ではありません。

x = 円の角度 \times 螺旋を巻いた回数 \div CVの数
x = 360 \times 11 \div 203
x \fallingdotseq 19.5

Angle にキーを打って動作確認します。ここでは1フレーム目を 、120フレーム目を 720 にしました。アニメーションを実行してみます。

dynamics02.gif

 回転運動との同期

糸の排出の制御は完成したので、次は糸の排出とコントローラの回転運動を同期させます。
 YarnTrailCtrl の Rotate X と Angle を同期させます。Angle に打ったキーを全削除します。再度 Expression Editor を開いて、最初の行に以下を追加します。

YarnTrailCtrl.Angle = -YarnTrailCtrl.rotateX;

これでコントローラのX軸回転と糸の排出が同期するようになりました。

動作確認のため、ヘアーを床に衝突させます。 nucleusUse Plane を有効にします。

Screen_Shot_2018-03-11_at_13_47_02.png

位置を調整して、X軸回転と移動方向にキーを打ち、アニメーションを再生させます。

dynamics03.gif

Curve warp デフォーマの適用

糸のカーブが物理で制御できるようになりました。ここからNURBSカーブにメッシュを適用させてレンダリングします。NURBSカーブにメッシュを割り当てる方法として、今回は Curve Warp デフォーマを使用します。Curve Warp デフォーマの分かりやすい解説動画を貼っておきます。

https://www.youtube.com/watch?v=_Ey8TSduH4U
https://www.youtube.com/watch?v=_Ey8TSduH4U

まずは糸のモデルとなるメッシュを用意します。Cylinder を作成し、アトリビュートを編集します。縦の長さは 2m、半径 1mm の糸を作成します。 縦の割りが必要になるので、Subdivision Heights を 200 に指定します。

Screen_Shot_2018-03-11_at_19_13_09.png

メッシュが用意できたら、 Freeze Transform をかけてヒストリを切っておきます。モデルを選択してから続けて出力カーブを選択します。

Screen_Shot_2018-03-11_at_19_26_32.png

Deform → Curve warp を選択します。

Screen Shot 2018-03-11 at 19.25.22.png

メニューに項目がない場合は、 Plug-in Manager でチェックが入っているか確認してください。

Screen_Shot_2018-03-11_at_19_19_56.png

螺旋にメッシュが巻きついたら成功です。

Screen Shot 2018-03-11 at 19.34.19.png

ここでレンダリングを開始したいところですが、 CV数の多い NURBS カーブに対する Curve Warp は重い処理で、容易にレンダリングの調整を行うことができません。そこでダイナミクスのキャッシュ化を行います。

ダイナミクスのキャッシュ化

キャッシュ対象の NURBS カーブを Outliner で選択します。

Screen Shot 2018-03-11 at 19.54.26.png

nCache → Create New Cache → nObject でオプション画面を開きます。

Screen Shot 2018-03-11 at 19.57.29.png

Cache FormatmcxFile DistributionOne File に変更して書き出します。

Screen_Shot_2018-03-11_at_22_57_57.png

レンダリング

毛糸のリグに球のメッシュを追加し、床とライトをセットアップして Arnold でレンダリングしてみます。
Arnold で書き出した動画を gif に変換しているので階調は粗いですが、球から排出される糸の動きが表現できていることが分かります。

dynamics04.gif

問題点と対策

今回の方法の大きな問題点は、シーンが重くなることです。シーンが重くなるとアニメーション作業に支障をきたします。
シーンが重くなる原因は、Curve Warp デフォーマの変形対象となるメッシュ量が大きいためです。しかし、メッシュ量を減らすと今度は糸の滑らかな形状を失います。なお、ダイナミクスに関しては、アニメーションをつけるときは nucleus を切っておけば、それほど重いシーンにはならないです。

解決案(暫定)
自分が作業しているときは、レンダリング用のシーンとアニメーション用のシーンを分けて管理しました。
アニメーションのデータは ATOM を使用し、ダイナミクスのデータは nCache を使用して、アニメーション用のシーンからレンダリング用のシーンにアニメーションデータを流し込んでいました。
nCache はファイルパスへの参照なので、アニメーション変更の際はキャッシュを書き出すだけの手間で済むのですが、 ATOM は毎回レンダリングシーン側で読み込み作業が発生するので工数がかかります。ここらへんは他に良い方法がないか模索している最中です。

おわりに

なかなか応用範囲の狭いマニアックなリグが完成しました。
github に今回作成したシーンを上げておきます。シーンを見たい人はダウンロードしてみてください。

8
9
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
8
9