これは先日アップしたVimeoのHD確認用ムービーと同じなのですが、元々はエクスプレッションの勉強も兼ねて、TPである1点から円状にパーティクルを発生させるにはどうしたらいいか考えて実際にそれを水面の波紋として使ってみようと思い色々実験したものです。感触としては非常にいい感じになったと思います。今回このプロシージャルな雨のシステムをTP rain systemと呼ぶことにしました。
今回波紋に関しては波紋用のパーティクルを一旦シーケンステクスチャとして真上からレンダリングし、それを水面のディスプレイスメントマップとして活用することで実現しています。それでは以下このTP rain systemの説明になります。 最初のborn rainダイナミクスでエミッターとなる板ポリからパーティクルを出すように設定しています。ここにすでにエクスプレッションノードがありますが、これはただsurface posノードから得られるノーマル情報を反転させることで板ポリの法線をひっくり返したような効果を得ています。でもまあわざわざこういうことしなくてもいいのでこういう方法もあるということくらいにしておいて構いません。ここではposition bornで雨粒用のrainグループのパーティクルを生成して次のダイナミクスセットに渡します。
次にmake waveダイナミクスです。ここは少々複雑です。まずrainグループのパーティクルがNodeによってUDeflectorに登録された水面のオブジェクトに当たった時にpositionアウトプットからはパーティクルが当たったポジションを、Collisionアウトプットからは当たった瞬間のonの状態を知らせるbool値が出力されます。rainパーティクルが水面に当たるとcollisionからonの情報がParticle Dieノードをonにし、そのパーティクルを消去します。それと同時にPosition born_smalldropノードから水しぶき用のsmall_dropパーティクルグループのパーティクルを発生させます。
それと同時にcollisionからのonの情報は波紋用のrippleグループパーティクルを生成するPosition Born_outer、Position Born_middle、Position Born_innerの3つのPosition bornノードに渡り各ノードがパーティクルを発生させます。各Position bornノードはlife spanノードでライフスパンを、shotノードでピストルショットの量をコントロールされています。次にDistansコンディションノード以下のノードがどういう働きをしているかですが、make waveダイナミクス解説用の2つ目の画像に一つの波紋用リングを生成させる仕組みをもう少しシンプルに分かりやすく描いたのでそちらをまず説明します。
まずこのDistanceコンディションノードで何をしているかというと、後のエクスプレッションノードで円状に配置されるパーティクルと水面に当たったパーティクルの位置関係を調べてそれをDirectionとして認識してるんですね。Distanceを使ってDirectionを出すときの約束事は"Position2が出発点でPosition1が到達点"です。右上に描いた簡単な図を見ると、青色のドットがPosition2つまり出発点、緑色のドットがPostion1つまり到達点で、オレンジのラインがDirectionとなります。今後Distanceコンディションノードを操作するときはこれをイメージすると分かりやすいと思います。Directionコンディションノードで得られたディレクションの情報がVelocityノードに渡され、波紋用パーティクルはrainパーティクルを中心に外側の向きで広がっていくことになります。
では次にどうやってパーティクルを円状に発生させているかです。これはmake_circleノードでエクスプレッションを用いることでやっています。このノードの中のR*[cos(a),sin(a),0]という式ですが、高校の数学で出てくる円と角度の関係の三角関数を表しています。画像の右下に半径が1の円(単位円)の場合円周上の点は(cosθ、sinθ)で表されることを示す図を描いているのですが、今回はこの三角関数に関しての説明は少し省いておきます。追々詳しく分かりやすく説明できればと思ってます。今回はそういうものだと考えてください。で、このmake_circleノードのインプットには角度θにあたる数字(scalar値と言います)aが、また円の半径を決める為のscalar値のRが入力されます。aの値にはRndom_angleノード(元はRondomノードです)で0から360までの値がランダムに入力され、RにはRnadom_Radiunsノードで指定した半径の長さが入力されます。Rndom_angleノードで例えば0から180を指定した場合は半円が描かれることになります。また今回はRnadom_Radiunsノードには1から1までと同じ値が入っていますが、例えば0,5から1とした場合は半径0.5から1の間にランダムにパーティクルが配置され若干太いラインの円が描かれることになります。
make_circleノードのoutアウトプットから出力された結果は次のset positonノードのV2インプットに送られます。make_circleでは単純に半径Rの円周上の位置を指定しただけに過ぎないので、実際に雨粒のrainグループパーティクルが水面にヒットした座標と足し合わせてあげる必要があります。これをするのがset positonノードです。Udeflectorのpositionアウトプットからrainパーティクルがヒットしたポジションがv1インプットに送られます。そして各座標が足し合わされた結果がset positonノードのv3アウトプットからPositionノードに渡され、Position bornで発生したrippleグループパーティクルのポジションとなります。こうしてrippleグループパーティクルが雨粒がヒットした部分を中心に円状に配置されるようになります。
さてまた一つ前の画像に戻って最後の説明をしたいと思います。rippleパーティクルはVelocityによって速度を与えられているのですが、一番外側のPosition Born_outerで発生するパーティクルにはglobal speedノードの値が直接入っています。一番外側なので一番速度が速く、2番目のPosition Born_middleで発生したパーティクルにはrate for middleノードで0.9ほど数値を乗算してあげてやや動きを遅くしています。それと同じ要領でPosition Born_innerから発生するパーティクルにはrate for innerノードでさらに小さな0.75ほどの値を乗算してあげてもっと遅い速度を与えています。このようにして3層の円が異なるスピードで円状に広がっていくように設定しているというわけです。
さて次のsurface followダイナミクスですが、これは単純にrippleグループのパーティクルをsurface followノードを使って水面上に沿わせているだけです。
次のgravity&prt dieダイナミクスでは、水しぶきのsmall_dropパーティクルが水面に衝突した時に消去しつつ重力を与えています。ここもシンプルです。
次はfrictionダイナミクスですが、frictionノードを使ってrippleパーティクルがやや減速するようにしています。
次はshape rippleダイナミクスですが、rippelパーティクルグループにシェイプとマテリアルとスケールを与えています。std shapeに関しては今回はトップビューからパーティクルをレンダリングするだけなのでカメラ方向を向くFacingを設定しています。Shape Materialは真上から白黒のディスプレイスメントマップとしてレンダリングしたいのでやや透明度を落とした白いマテリアルを当てています。
次はshape rainダイナミクスですが、ここでは雨粒のrainパーティクルグループと水しぶきのsmall_dropパーティクルグループに対してシェイプとマテリアルとスケールを与えています。
以上がTP rain systemの説明になります。ここからVrayマテリアルの設定とかシーンのライティングの設定とかに入ってくると収集がつかないのでここでやめておきます。恐らく今回説明した以外にも波紋を作る方法や、さらにはわざわざディスプレイスメントマップをレンダリングしなくてもいい方法もあるかもしれません。そういう方法が見つかったらまた追記していきたいと思います。
ちなみに今回はDirectionコンディションノードの説明やベクトルの簡単な計算、エクスプレッションで使う数式などを扱いましたが、慣れてくれば色々表現の幅が広がってくると思います。ちなみにMAXで使えるエクスプレッションのマニュアルはMAXヘルプからExpression Techniquesと検索してみてください。エクスプレッションに関する約束事が細かく明記されています。エクスプレッションに関しては理系出身じゃなくてもここはぜひマスターしたいところです。かく言う自分も数学物理は非常に苦手です^^;でもできるだけこのブログでは数学物理が苦手な人でも分かりやすいように記事を書いていければと思っています。

