<< TOP



Twitter 100Followers 記念
は・じ・め・て・の  ICE Simulation その1
スプリング/コリジョン編
 (My First ICE Simulation)
                

 プロローグ
      今回は、より複雑な処理を実現させる ICEによるシミュレーション機能 についてに記述しようと思います。

     ICE は ビジュアル・プログラミング ということで、ノードの組み合わせでパーティクルを制御できる訳ですが、
    このパーティクルを ”位置の集合体” として捉えると、
    様々な位置データを ICEのパーティクル・シミュレーションを使って制御する・・・という風に考えを変える事が出来ます。
     ICEのパーティクルには、フォース や RBD(リジッド・ボディー・ダイナミクス;物理計算) などたくさんの制御方法が用意されているので
    ICE機能をうまく使えば 位置データとして 色々と処理することが可能となってきます。 おもしろそうでしょ♪♪

      ICE Tree を付ける場所も 取得>プリミティブ>ポイントクラウド>空 から作成される pointcloud に設定します。
    pointcloud に付けた ICETree は、実は シミュレーション・コンストラクション・モード以下に作成されている事に気付きます。



     もう一つ覚えておくべき事は、この pointcloud に付けた ICETree でないと利用出来ないICEノードもあるということです。(パーティクル制御だから・・)
    その代表的なものに、Strands(ストランド) なんかがあります。発生位置から連続した線を計算(描画)します。
         >> 詳しくは、Softimageのユーザーガイドの Create Strands(ストランドの作成) 項目 サンプルシーン Strand_Dynamics_Hair.scn 参照 

     この分野でのICE技術の利用はたいへん明るい未来が約束されており、
    アイディア次第では アニメーション や Rig や モーション など様々なナイスな制御方法を生む事間違いなし!!、の技術です。

     さー、ICEのスプリングやコリジョンの制御方法を学び、 位置データ制御をICEで行う際の基礎知識を習得しちゃいましょう!!

    1)  位置データとしてのパーティクル
     > Spring Force の利用1
     > Spring Force の利用2
     > Spring Force の応用例;アホ毛
     > コリジョンを簡単に付ける例;アホ毛
     > Spring Force の利用3
     > Spring Force の利用4
     > Spring Force の利用5
      パソコンのスペック WindowsXP SP3、 Intel Core2Duo 3G、 RAM; 3G、NVIDIA Quadro FX1700


位置データとしてのパーティクル
  ここからの幾つかのサンプル・シーンの データ提供は ; PSYOP Todd Akita さんからのものです。 Thank you Todd_San !!
同様の解説をしているムービーも存在しますが、この記事では更なる探求や応用例を紹介しています、それにココはやっぱ日本語だしね。

 Spring Force の利用1
      確か、この話をする時に最初に解説してもらったのが、フックの法則(英: Hooke's law)でした。(弾性の法則)

          F=-kx
              F; ばねによる反力  ;ニュートン
              k; ばね定数と呼ばれる定数 各ばね固有の値、ばねの強さ ;ニュートン毎メートル
              x; つる巻きばねの何もしてない状態の長さから 伸び/縮みした分の距離 ;メートル

    だとさ。つまり、バネ定数 K と 伸縮する距離 x を設定することでバネらしい動きを再現できる、ということでした。

     では、まずは簡単な バネっぽい動きをするパーティクルの作成から始まります。



     PointCloud に ICEシミュレーション の ICETree を作成し、
    パーティクルのタスク内  Emitters > Emit from Surface を取り出して、
    GetGrid で グリッドを発生場所(Emitter1)として接続します。
    Emit from Surface 内の項目では、
    レートの種類を [パーティクルの総数] にし、パーティクルの発生数を [10] 個とし、速度を限りなく [0] に近い数値(0.3)を設定しています。
    0 にするとシーン再生時に全く動かなくなりますので、その方が良い場合もあります。Shape は見やすく為に Spereにしています。
    Getters > Get Partivle Emit Location で発生したパーティクルの位置を取得し、そのPositionを SpringForce に接続します.
    SpringForce のノードで バネ定数 K としての値を 10 としています。

       この SpringForce コンパウンドの中身は、下図のようになっています。(発生地点と現在の位置を割った距離にK値を掛けた位置)
      簡単なものですが、たくさん使いそうでしたらコンパウンド化して登録しておくと便利です。 >> Spring Force.xsicompound



     Forces > Drag Force と共に、 Forces > Add Forces  に接続します。
    Drag Force はつまり減衰値になります。ここを 0 に設定すると ずーーーと 上下にバウンドしたままになります。
    最後は ツール>Simulation> SimulateParticles を一番下の最後のPortに接続します。これが無いとパーティクルが生まれません。

     これで設定が完了していますので、シーンをプレイバック状態にして(ループモードにすると良いです)、Gridを動かしてみると、
    10個のパーティクルが ぼよよーん と追従して来ます。

    Spring K と Strenth の値を変更することで色々な動きに変化します。

     バネらしい動きとは、つまりこの ”減衰度合いをうまくコンロトールすること” になります。
    そこで、この減衰する部分のノードを更に追求したのが次の例です。



 Spring Force の利用2
     さー、ぐーんとノードが増えて来ましたが、前半は同じです。
    改良されているのは、減衰部分の SpringDamperForce ノード以下です。



     最初の例で利用しているデフォルトで存在する Drag Force の中身を見ると、実は結構複雑なのです。
    ここでの SpringDamperForce というコンパウンドの中身は たったこれだけです。



     VelA には GetDataで self.pointvelocity の値を接続しています。
    パーティクル・タスク内の Getters> GetParticleVelocity でも同じです。パーティクルの速度の取得 だと解ります。
     次は少し長いですね。
    GetDataで Self.EmitLocation で放出ロケーションを取得して、
    ツール内> DataAccess>  GetDataAtPreviousFrame を取り出して pointposition を取得しています。
    これは前フレームでの位置、下は 現在の位置を取得して
    2つの入力値の差(減算値)に ツール内>DataAccess> SimulationStep 数を掛けています。その値が VelB に来ています。
      シミュレーションのステップ とは 1フレームの長さを秒単位で出力したもので、
    フレーム数と秒数を変換する場合、またはフレーム単位で表された属性と秒単位で表された属性を変換する とヘルプに書いてあります。
    Spring kd には 1 が入っています。 
     つまり全体では 秒単位距離を速度で割った値に定数を掛けた ということらしい。
    結果、この Springkd の値を小さくすると、バネの振動が収まる時間が長くなる という風に制御されました。(減衰値なので、値が大きいとはやくおさまると・・)
     Springkd の値を 0.5 にして Grid を動かすと、かなり プルンプルン♪ な動きになります。楽しい!!



 Spring Force の応用例;アホ毛
     日本文化(?)らしく、この Spring Force から作成されたバネの動きを キャラクターのアホ毛に適応する例を紹介します。
    アホ毛とは、キャラクターの頭から数本ピョンと突出た毛のこと、
    英語では Frizz って書いてあるが本当に通じるのか?どっちかって言うとそれは縮れっ毛? 英語に出来ないからこそ日本文化なり。

     以下に記述している点を知っておくと、ノードを接続できなかった時に 「はた!!」 と気付く事多いです。
    それはパーティクルの位置データを ポリゴンやNull等オブジェクトのグローバル値に直接つなげようとした時に起こります。
    パーティクルのPointPositionはマルチポイントのデータ(1個しか放出していなくても) なので直付けできないのです。
     白枠の部分がその設定です。
    では詳しく見てみましょう。 あ、パーティクルの総発生数は 1 個 になっています。

     まずは ”頭” を想定した Sphereの子供にボーン構造を作成し、
    同じく発生源であるGridを小さくしてSphereの子供にし、レンダリングされないようにVisibilityを変更し、ボーンのエフェクターの位置に設定しました。
    Gridのセンターは ”頭” を想定した Sphereの位置にあります。



    ICETreeの白枠の設定は、GetData でエフェクターのグローバル値を取得し、Matrix to SRT でSRTを分けておきます。



    ここ重要!! GetData で self とだけ記入し、それを ツール内>Conversion> ID to Location ノードの Geomertyに接続します。
     発生したパーティクルは、たとえ1つだったとしでもそのPointPositionは複数の値用のデータになっています。
     発生させた最初のパーティクルの ID値は 0 だと解っています。
     つまり、ここの設定は、己の見て ID=0 のパーティクルのLocationを取得して、パーティクル1個の値をGetします。
    [値の表示] をすると、ちゃんと ID=0 のパーティクルのグローバル値が表示されます。
    あとは簡単ですね。その値を エフェクターのグローバル値に代入してやります。

     仮にキャラに適応してみました。 再生しつつ、あるいは画面Captureでムービーを見つつ、動きの調整をしていきます。
    ポリゴン数の多さにも関わらず意外に軽く動きます。調整すると大きい数値(バネ定数)になりました。
     設定としては、Headの子供としてスケールを小さくした発生源であるGridを置き、パーティクルのSizeを0にしています。
    Massサイズはそのままが良いです、0にすると吹っ飛んでしまいます。



     ということで、久しぶりにキャラのアニメになりました。
    髪の毛のゆれを手付けのアニメーションで無いはじめての例になりました。 胸やリボンや後ろ襟などたくさんの利用が考えられます。




 コリジョンを簡単に付ける例;アホ毛
     さて、その次に、この位置として使うパーティクルにコリジョン(衝突判定)を付けるには、すごい楽な方法があります。
    ICETreeのPortの最後に接続している SimulateParticle を SimulateRigidBodies にしてしまうのです。
    その中のOcstracle(衝突するもの)に ポリゴンのジオメトリーを接続してしまいます。
     今度は、パーティクルのサイズで衝突を判定するので大きさが必要なのですが、
    PointCloud自身にマテリアルを設定し、ハイライト無しのアルファーを設定して抜いてしまえば、
    パーティクル自身をレンダリングしないようにできます。



     次の例では、複数のパーティクルの動作制御になってきます。


 Spring Force の利用3
     さて、これも前例の続きなので、右側半分はもう解説しています。
    この動きは、10個のパーティクルが発生し、最初のが 箱表示になっているNullにくっつき、
    あとのはID番号順に次々と列を作って整列するするよう という風になっています。
     逆に言うと、Nullを動かすと、次々とパーティクルが追従して来るので蛇みたいなんですが、
    スプリング制御で動いているので、パーティクル同士の間が大きくなると速度を速めて狭め、間がつまると動きがゆっくりになります。
      ここにも知っておくべき項目がありますので解説してみましょう。



      これ↓、面白い設定方法ですね。 解りましたか・・・。パーティクルはマルチのデータです。
    己の ID を GetData していくのですが、最初のパーティクルは 0 と = なので、下行って、
    Nullのグローバル位置データをGetDataして それをパーティクルの位置データとし、
    それ以降の ID は 当然 0 じゃ無いので、なにもしない。ですって。
     つまり、一番最初のパーティクルだけに Null位置データを渡したかったのですね。



      お次は、もっと不可思議、己のID値の 下の 1 は 列数になるんです。
    ID値から得たPointPositionをそのまま SpringForceにつないでしまうとみんなNullの位置になってしまうところを、
    間に 空間のベクトル値 (GetPosition の後の Subtract の Second に つないでいる "Spacer"Vector を入れ込んでいて、その分 間が開いて整列します。



    仮に、最初の数値が 3 、SpaceVectorに Z=2 だとこうなります。



      既存サンプルとしてはここまでのものでした。

     ここからこれを元に ボーンを組み込んでコリジョンまで対応させてみましょう。 
    どんな風に既存サンプル技術を目的とするような動きに応用させるのかの例としてみて見てください。面白い技たくさんあります。


    ↑ こんな感じが目標です ↑
     では、行きます。
    まずは、ICETreeノードを2つに分けることを考えておきます。
    1つは上記のパーティクルを生むICETreeを Simulation_ICETree と名前を付け、
    もう1つのパーティクルにオブジェクトをくっ付けるICETreeを Constraint_ICETree を命名したとします。
    パーティクルの発生源が Grid なので、そこからの位置で制御するように変更しました。
    Nullからの位置データではなくでGridからの位置データを If ノード下に SetDataで渡します。
    パーティクルの発生も、[法線方向] とし (つまり、今このGridは下が正面)、発生個数を 6 個 、Speedに 5 を入れて速く整列するようにしました。



     CEDEC2010では、「”重力”という要素を考えてアニメーションを付ける」、
    ということがなんとなくのあちこちから聞こえて来たキーワードの1つだったので、
    ココに重力フォースを追加してみました。
    と言っても、中身を見ると Y に -9 が入っているだけの  3DVector だって解ります。
    追加で Vectorの方向を他のオブジェクトからの回転値で制御できるノードとして RotateVector を紹介しておきます。
    その結果を [値の表示] で 表示を Vectors にすると Gridの回転値でVectorの方向が変化しているのが見てとれます。
    とっても便利なノードなので覚えておいてください。
      この例ではスプリングに制御されてしまうので、大幅に方向を変えることができませんが、少し膨らみを持たせることが出来ます。



    残りの設定で変更したのは、Springを強くして 30 、間の距離を ”Spacer Vector” に 2 (1.8は試し数字でした) 、減衰用 SpringDampForceに 2 を入れてます。



     では次はボーンをパーティクルにくっ付ける方法の考察です。
    まずは、6個のパーティクルに対して、取得>プリミティブ>インプリシット>ボーン から 5つのボーンを用意し、
    それらが内包するGroupを作成しておきます。



     上記で、1つのパーティクルに対して1つのオブジェクトの位置を渡す方法で 5つのボーンをくっ付けるとこんな感じになります。↓
    なんか、うーんな感じしませんか? あと、ICEのIKで説明した通り、SetDataを大量に設定することは処理的に効率的では無いと・・・



     また、各ボーンには回転値が考慮されてないので、皆ストレートに下向きのボーンになってつながっています。
    これはパーティクルの位置に置いてあるだけなので当然ですし、元々階層構造のボーンでも無いからですね。ここにも工夫してみましょう。
    そこで、そうです、あれです。ICEIKで説明した Set in Array を使った高速化方法を利用するのです。
    出し惜しみをしてもしょうがないので、先にそのICETree構造を見せて解説していきます。

     考え方の大前提として、ここもマルチポイントのデータですので、1つ設定で6個全部のデータを処理します。
    PointCloudに2個目のICETreeを設定し Constraint_ICETree を命名します。
    値を格納しておく箱として 4x4Indentity と BuildArrayFromConstant から得たデータを 
    SetDataで BoneTransforms という新しいパラメータを作成して、そこに接続し(流し込み)ます。



     ボーンのグループ bone_group をExplorerからD&DでノードをICETree上に持って来て、
    そこから GetDataで ICEBoneParamsのBoneID を取得します。 (この解説はICE IK を参照、グループを選択してスクリプトを実行すれば作れます)
    そのBoneID値を Set in Array の Index に渡すと共に、下の ID to Location のID値にも接続します。
    これでBoneID値と その時のパーティクルIDから判別する位置データを同時に設定出来たことになります。
     (つまり、BoneのIC値が 0 の時、パーティクルのID値が 0 の時の位置データが設定されます)

     お次が、骨の回転値です。
    こう考えると簡単ですね、二点間の位置データから方向 Vector を得られればと。
    これには Increment Rotation with 2 Vectors を使います。
    ここでの使い方は、Increment Rotation with 2 Vectors の 最初の Rotation にはGridの回転値を入力しておいて、
    パーティクルのIDの位置データと そのID値に1足した位置データ (つまり 例えば ID値 0 と 1 ) 
    の位置の違いを Increment Rotation with 2 Vectors の ToVector につなぐと回転値を計算してくれます。



        ICEノード機能の探求のコツ
               上図右下に使ってないノードが2つあります。    ↑
              完成したノードのつなぎ方にたどり着く前に、その機能について探っていた時に見ていたコンパウンドなのです。
              コンパウンドは複数のICEノードを組み合わせて機能を達成させている実例になっています。
              つまり、その中身を見てどんなノードをどういう風に組んでいるのかを見ることによって、
              各ノードの使い方を知ることができるのです。

              ちょっと調べ方の例として、今回はこんな風に探りました。

               例えば、2点間のコンストレイント なんて機能のコンパウンドがあれば、
              きっと2点の位置と回転値から導き出しているに違いない・・・と考えます。
              すると、Kinematics>ConstrainBetween2Points を探せ出せます。
              このコンパウンドの中身を見ると・・・ 
               なんだ  Interpolate Pose Between 2 Positions というコンパウンドが 
              2つのグローバル位置をブレンドしてるんだ ・・と気付きます。



               この Interpolate Pose Between 2 Positions もコンパウンドなので、更に中身を見ると・・・
              おお、あったあった、
              ・回転値を最初につないだものをそのまま使うか、
              ・Increment Rotation with 2 Vectors で得た2点間から計算した回転値を使用するか、
              を IF ノードで選択し、
              位置は二点間から計算した結果を移動値としていて、
              最後に SRT to Matrix でグローバル値に結合にして出力している・・・ とわかります。 とっても、便利ですね。



              こんな風に追うと使い方が解るんです。 
              普通はここまでで、これを便利に応用すれば良いかと思います。


                -*-*-*-*-*-
                でもね、本当はもっと複雑なのです。ここからはプログラマーさん向けです。
              実は、Increment Rotation with 2 Vectors もコンパウンドなので、更に中身を見ると・・・



               ちゃんと 回転値をクォータニオン変換して計算してから回転値に戻しており、
              なにやら RotateArc なるものが介在していると・・・・知ります。これもコンパウンドです。
              これにはコメントも付いていて 
              Taken from the Game Programming Gems article 'The shortest Arc Quaternion' by Stan Melax とあります。
              上記書籍にて このクォータニオンの計算について 日本語では 最小弧クォータニオン と書かれています。
              この RotationArc() という関数は、2つのベクトル V0 と V1 から q*v0==v1 を満たす クォータニオン q を計算するものです。
               quaternion RotationArt(vector3 v0,vector3 v1) 以下が ICETreeのノードで実現しているんだ と知ります。 


               上の CrossProduct で軸計算でVector、 DotProduct で角度計算で数値、Scalor to Quaternionなんてデフォルトでノードがあります。

               プログラマーさんで、クォータニオン計算をICEで実装しようとする方はここをぜひ参考にして欲しいところです。
              ここでのノード使用例でも、上図Vector1からの値が入力されて計算結果であるRotation値を使用していることになります。


     さて、話を戻します。
    で、コンストレイントに使用する BoneTransforms 値 を ID値と合わせて準備できました。
    今度は各ボーンに 己に持っているID値から 対応する位置データを取得して 自分のグローバル値とする 部分です。
    これは、もう ICE IK のところで既に説明しているのです。



     己の BoneID値 を GetData し、それを SelectArray の Index に接続します。
    PointCloudから集めた BoneTransforms 値を GetData し、SelectArray の Array に接続します。
    そこから得た位置データを その骨のフローバル値として SetData します。
     これを各ボーンに設定するのですが、黄色い枠内はいつも同じで、何も変化がありませんので、
    コンパウンド化してくっ付けている例が ICE IK のところで書いてあります。
    どうでしょうか? ちゃんと骨が動いていますでしょうか?

     最後は、ちょっと安直ですが、
    SimulateParticles を SimulateRigidBodies に変えて 球を障害物として設定してみました。
    くっつき度 Elasticity1 は0にしてあります。




     動かしてみるとこんな感じになります。
    伸びたり縮んだりする例になるので、紐とかゴムとかの付属物のアニメーションに良いかも知れませんね。
    あるいは、思いっきりピーンとさせて文字通りバネに使用するかです。




 Spring Force の利用4
     これも面白いSpring利用例になります。
    下図は、エンベロープの付いているポリゴンとそれの複製に ICE を設定し、
    エンベロープのアニメーションが止まっても、ICEの方が びよよーん と動きが残るようになっています。
    アニメーションの時は、エンベロープの付いているオブジェクトを非表示にしておくというわけです。



     解説としては、エンベロープの付いている方のポリゴンの PointPositionを GetDateで取得し、
    Conversion > Switch Context という珍しいノードを使用して SpringForce に接続します。
    この Switch Context (コンテキストの切り替え) は、2つのオブジェクトが全く同じジオメトリ構造同士でないと動作しないのですが、
    同じであれば、位置のブレンド等入れ替えが出来るノードのようです。
     で、Switch Context で頂点位置のデータを受け渡したら、SpingForce で動きが計算され、びよよーん が残ると。
    減衰用のDragは 前述の少し変えたもので、
    Getters> GetParticleVelocity でパーティクルの速度
    Math>Basic>Negate 否定演算(符号を反転)、DataAccess> SimulationStep は1フレームの長さを秒単位で出力しています。
    つまり秒単位あたりの距離 となっています。



     ポリゴン自体の ぼよよーん 動きの例としては、トランポリン や 太鼓 、破壊される破片がブヨブヨしている などの使い道がありそうです。

    複製元の頂点位置データを取ってきているだけ・・なので、きっと ShapeAnimation もOKなはずです。
    実際に実験してみましょう、トランポリンで。
    Softimageの ShapeManager はとっても簡単便利、元の形と、それの真ん中を引っ張った形を登録すれば、
    勝手にスライダーができるので、たった1フレームで元に戻るというアニメーションをそのスライダーのキーアニメーションで作成します。



     あとは、上記 ICE の Sping を何もアニメーション設定のしていない複製したポリゴンの方に設定し、
    Shapeアニメーションの付いている方は 非表示にします。



     ということで、ジオメトリーが同じということの典型である ShapeAnimation を使って
    ICE による Spring 設定が出来ることが解りました。



 Spring Force の利用5
     さて最後です。
    これは純粋にパーティクルの話です。
    発生した地点を得て、定数をTurbulize(乱れ値) した値で SpingForce を設定しているので、動きの延滞がおもしろい動きをするよ、というものです。
    減衰値の方にも Particleタスク>Modifiers> TurbulizeAroundValue がついています。
    30000個もパーティクル発生していますが、意外と動いて見れるものです。



    Particleタスク>Modifiers> TurbulizeAroundValue の設定値です。



     これはもうレンダリングを想定した設定で、ジェリー状の付着物、まとわり付く煙など 考えてみましょう。


     ICEによる シミュレーションはいかがでしたでしょうか? 位置の処理にICEの シミュレーションを使うということが理解できたでしょうか?
    実は全く別のICE設定の Spring も書く予定でしたが、これだけでもお腹いっぱいな感じです。
    そちらは更に設定が高度なのでじっくり説明したい・・・・ので、次回としましょう。 




     という訳で、次回も  ICE Simulationネタになりそうです。
      乞う、ご期待!!