2013年12月10日火曜日

FumeでDfaultシミュレーションからレンダリングまでを一気に(?)行うTips

だいぶ更新が止まってしまいました…。ありがたいことになんとか元気にやっております。

今回はFume(3dsmax)でDefaultシミュレーション>Waveletシミュレーション>レンダリングまでを一気にローカルマシンで行うスクリプトTips
バックバーナーでシミュレーション後にすぐレンダリングを行うTipsです。
(3dsmax2014とFumeFX3.5.2で試していますがほかのバージョンでも行けるかと思います。)

「このシミュレーションが終わってレンダリングをかければ帰れるのにあとちょっとで終電を逃した~」などなくなります!

帰宅時にシミュレーションかけて翌朝レンダリングを確認したいときにすこし幸せになれるかもw
そもそもマシンパワーでエラーが出て落ちていることも多々ありますが…^^;
金曜の夜にかけて月曜日確認しようと思って職場に来てみたら落ちている時の虚しさといったらないですね…(ToT)

■ローカルマシンでシミュレーション>レンダリング済ませるのスクリプトを紹介

●Fumeのグリッドを選んで下記スクリプトを実行。
----------------------------------------------------------------
$.SelectedCache = 0     --①Cacheモード(?)をDefaultにセット
$.RunSimulation 0    --②DefaultSim実行
$.RunSimulation 2    --③WaveletSim実行
$.SelectedCache = 1  --④Cacheモード(?)をWaveletにセット
rendUseNet = off     --⑤ネットワークレンダリングをオフ
max quick render     --⑥レンダリング
----------------------------------------------------------------
もちろんシミュレーションキャッシュパス、レンダリングパスの設定などをきちんとやった上で実行してください。
(テストしている時、何度かWaveletを使用しないでやろうとしていましたw)

●Cacheモード(?)をDefaultにセットしてからやる場合は①なくてもOK
※Waveletでテストしたあとにそのままにし忘れて回らないミスを防ぐためです。

●Waveletシミュレーションをしない場合は②と⑥だけにして実行でOK.
●ネットワークレンダリングをオフになっていれば⑥はなくてOK
※オンになっているとバックバーナーにサブミットしようとするので…。
(もちろんシミュレーション、レンダリングのパス設定を行った上で実行すること。)

■シンプルにすると…(上記の①と⑤はミスを防ぐためなので)
----------------------------------------------------------------
$.RunSimulation 0
$.RunSimulation 2
$.SelectedCache = 1
max quick render
----------------------------------------------------------------
です。

■Waveletシミュレーションしないで通常のシミュレーションとレンダリングだけしたい場合は…
----------------------------------------------------------------
$.RunSimulation 0
max quick render
----------------------------------------------------------------
だけです。

簡単ですね。
なんで今まで使わなかったんだと思うくらいですw
複数でやりたい場合はForLoopで回せばできると思います。

いきなり実際のシーンで、やって翌朝回って無かったとかならないようにw、念のため簡単にテストしてからトライしてみてください。



■スクリプトではないですが、バックバーナーでシミュレーション後、レンダリングを回してしまいたい場合は…

(Fumeでバックバーナーにシミュレーション投げる方法はマニュアルや他の方ブログを参考にしてください。)

バックバーナーモードでシミュレーションを投げたあとレンダリングの設定にしてバックバーナーにジョブを投げる際、下記のように設定

ちゃんと設定できているとレンダリングのジョブに小さな「+」(プラス)観たいマークが付きます。
もちろんバックバーナーに投げたあとキューモニターからも設定できます。

まぁ、この方法でなくてもシミュレーションジョブと同じマシンにレンダリングのジョブを投げてしまえばいいのですが、それだと一台でしか回せないですし、
この方法はバックバーナーのジョブの従属を決めるものなのでシミュレーション以外にも使えますが、あまりやってる方を見かけないようなので紹介してみました。
Vrayでイラディアンスのキャッシュ計算>実際のレンダリング
KrakatoaでPRTの保存>PRT読み込んでレンダリング
などにも使えますので知っておいて損はないと思います。


※スクリプトはあくまで自己責任で、バックバーナーの方は詳しくはマニュアルなどを見てください。


今お仕事でFume使う頻度が増えたので、もっとスクリプトで楽チンできないかと思い色々スクリプトを探っているところです。
ちょっとしたスクリプトも作っているので完成したら公開するかもです。
一応出来てはいるのですが一部仕様変更をしたいので。少々お待ちを…。
気まぐれなので公開しないかもですが。。。
興味がある方はコメントまたはメールをください。

2012年4月4日水曜日

パーティクル選択ツールを作りました。-ThinkingParticle/ParticlFlowSelectionTool-

またまた更新があいてしまいました。
今回は自作ツール「ParticleSelector」および「ParticleFlowSelector」を作りましたので公開します。
機能は単純でパーティクルの選択、オンオフの切り替え、パーティクルビューの表示(TPダイアログの表示)だけです!
見た目もシンプルでコンパクトにしました。

TP用にはすでに「TPtuner」というすばらしいスクリプトが公開されています。
http://www.scriptspot.com/3ds-max/scripts/tp-tuner
これはかなり便利なツールです。
実際こちらのスクリプトを色々参考にさせてもらっています。
今回公開したスクリプトはこのスクリプトの超簡易版です。
「TPtuner」はとっても便利で色々出来るので使い勝手は良いですが、ウインドウが大きく画面の結構な範囲を占めてしまいます。
今回公開したスクリプトはもっとシンプルに使いたい場合に重宝すると思います。


スクリプトはこちらからダウンロードしてください
ParticleSelector.mcr
ParticleFlowSelector.mcr
max2010,2011,2012日本語版検証済です。
※念のため使用の際は十分注意してください。また使用の際何らかの損害を被ったとしても一切の責任を負わないものといたします。ご了承ください。
エラー報告、機能追加の要望などあればお願いいたします。



■「ParticleSelector」はThinkingParticleとParticleFlowに対応しています。
上がTPのリスト、下がPFのリストです。
■「ParticleFlowSelector」はParticleFlowのみの対応です。
ThinkingParticleを使わない場合はこちらをお使いください。
ThinkingParticleが無い場合でも「ParticleSeletor」を使っても問題ないですが、使わないのにTPのリストがあっても邪魔かと…。
逆にTP使う場合、PFlowも使う場合も多いかと思ってTPのみのバージョンは公開しません。


■インストール方法maxScriptを起動からダウンロードしたスクリプトを起動してください。
(普通のマクロスクリプトと同じインストール方法です。)
一見何も起きませんがインストールされています。

■カスタマイズ>ユーザーインターフェイス>ツールバーやショートカットなどに登録してください。
「Imazu Tools」という恥ずかしい名前のカテゴリーになっていますw





 ■リストを左クリックでパーティクルを選択します
※複数選択には対応していません。またTPのリスト、PFのリストを同時選択は出来ません。

 
■リストを右クリックでパーティクルのオンオフを切り替えます。
TPの場合MasterDyamaicのオンオフを切り替え






PFの場合はParticleSouceのオンオフを切り替え 

■リストをダブルクリックでパーティクルビューを開きます。
TPの場合はThinkingParticleDialogを開きます
PFの場合はParticleViewを開きます


■Refleshはリストをリフレッシュします。
パーティクルを削除した場合や追加した場合などリストを更新したいときに使います。

(実はこのリフレッシュが結構手間取りました。
でもその甲斐あってわざわざリフレッシュボタンを押さずともリストを選択したときなどにも更新されるようにしました。
リフレッシュボタンも省こうかとも思いましたが、念のため残しました。)


■Aboutはバージョン情報や簡単なヘルプです。







今後、もし出来れば追加したい機能は…
パーティクルをオフにしたときにリストのパーティクル名をグレーする機能。
パーティクルビューの開閉の切り替え機能をつければとおもっています。
(開くのは出来たのですが閉じるのがわかりませんでした…orz)
単純な気がしますが、そもそもスクリプトのコード(?)に無いような気がします。
もしご存知の方がいましたら教えてください。

以上です。
スクリプトの勉強もやはりこまめにやっていないと駄目ですね…。毎度一からやり直し状態が続いて、出来たと思ったらエラーつぶすのに時間かかったりしています。
スクリプトの勉強は似たようなスクリプトを探して中身を解析して自分のスクリプトに取り込んだりするのが手っ取り早い気がします。 今回もそうして作っています。

スクリプトの中身を見ていただければ、わかる方にはわかると思いますが、雑だったり無理やりな部分も多々あるかと思います。
毎度ながらアドバイスなどありましたらよろしくお願いいたします。

2012年2月22日水曜日

ThinkingParticlesでオブジェクト同士の設置箇所からパーティクルを出す方法。

なかなか手が動かず、久々の更新になってしまいました…。

今回はオブジェクト同士の設置面からパーティクルを発生させる方法です。
作例は船の航跡をイメージして作っています。
航跡のほか波打ち際などにパーティクルを発生させたい時の参考になれば。

TP FoamTest from take-imazu on Vimeo.



■元となるシーンはティーポットがパスに沿ってアニメーションして水面を進んでいるようなシチュエーションです。


■まずベースとなるオブジェクトにパーティクルを配置します。
①パーティクルの発生源となるベースとなるオブジェクトをTP内で扱いやすくするため、パーティクル化します。

ObjToParticleオペレーターでティーポットをパーティクル化します。グループはNodeobjグループです。

②ベースオブジェクトからパーティクルを発生させるための種となるパーティクルを作成します。

PositionBornオペレーターで数が50速度0のパーティクルを作っています。グループはSeedグループです。
発生させたパーティクルはこの時点では速度も無いのですべてのパーティクルが原点にある状態です。

③ベースオブジェクトをパーティクル化した物(Nodeobj)の表面に種(Seed)となるパーティクルを配置します。

PPassABオペレータのグループAにNodeobjをグループBのSeedを指定します。
SurfacePosヘルパーとPositionオペレータを使ってNodeobjにSeedを配置します。


■配置したパーティクルを水面より上と下でグループを分けます。
④水面オブジェクトの内側にあるパーティクルはUnderグループ、外にあるのはOverグループとして切り分けます。

SeedグループをInMeshコンディションオペレータとNodeヘルパーを使いグループを切り分けます。
InMeshのノードはノードヘルパーでwaterオブジェクトを指定、パーティクルがMeshの中に入ってonになったときUnderグループに送られ、Invertヘルパーを間に挟んでoffになった時はOverグループに送られようにします。
こうすることによってオブジェクトが水面で上下してもきちんとグループを切り替えてくれます。


■分けたグループを元にPPassABを使って間のパーティクルを抽出します。
⑤PPassABで先ほど切り分けたUnderグループとOverグループを指定して、Distansでグループ同士の距離を指定、その距離内にあるパーティクルをMidグループに送ります。

PPassABのアウトプットでどちらのグループに繋ぐかでどちらのパーティクルから抽出するか決めることが出来ます。両方をつなぐこともできます。
今回は水面より上のパーティクルを抽出したいのでグループAで指定したOverグループからパーティクルを抽出し、グループオペレータに送ります。
送られたパーティクルはmidグループになります。(説明が難しい…)


■抽出したパーティクルからアニメションさせるためのパーティクルを発生させます。
⑥midグループはまだ抽出しただけなので、ティーポットに張り付いたままです。
このmidグループから新たなパーティクルを発生させます。

midグループにPositionBornを繋いで新たなパーティクルを発生させます。
グループはSpawnとします。

■新たに発生したパーティクルをダイナミクスを与えます。
⑦重力を与えます。

SpawnグループにForceオペレータとPoint3ヘルパーを使って重力を与えます。
この方法は皆さんよく使われているようです。

⑧水面に当たったパーティクルが水面を沿うようにします。
また、水面にうまく当たらず下に落ちていくパーティクルが発生してしまったので、下に落ちていくパーティクルを消すため、水面に当たったパーティクルを新たなグループに送ります。

UDeflecterオペーレーターとNodeヘルパー、SurfaceFollowオペレータで水面オブジェクトにパーティクルが当たったら、水面オブジェクトに沿うようにしています。
水面に当たったパーティクルをグループオペレーターでFoamグループに送ります。

■余分なパーティクルを削除します。
水面にうまく当たらず水面より下に落下するパーティクルが出来てしまったのでそれらを消します。

⑨Spawnグループで水面より下に入ったグループをInMeshコンディションで水面オブジェクトを指定、onになったSpawnパーティクルをParticleDieオペレータで削除しています。


もっと早い段階で余分なパーティクルを消すことが出来ればもっと効率的になるのですが、うまいやり方が出来ませんでした…。


いつも思うのはこの先の段階のパーティクルの振る舞いをコントロールするのが本当に難しいですね…。

2012年1月27日金曜日

前回の記事の問題解決方法。

前回の記事で書いた問題が解決しましたのでその報告です。
前回の記事を読んで少しでも解決策を考えていただいた方、アドバイスをくれた方ありがとうございました。

では解説です。
まずどういったことがしたかったのかわかりづらかったようなので説明いたします。
■シチュエーションとして水面などからオブジェクトがでてオブジェクトの表面をパーティクルが沿って下に流れ落ちるという物を作りたかったのです。
(ムービーではオブジェクトの表面に沿っていませんが…)
■そして流れ落ちるパーティクルがどんどん減って行きたかったのです。
■また、データを軽くするため水面下にあるときはパーティクルを発生させたくありませんでした。

■念のためベースとなる仕組みの説明をします。
まず元となるシードのパーティクルを作ります。
オブジェクトの表面にパーティクルを配置し、アニメーションについて行くようにしています。
表面に発生させたパーティクルからスポーンを出しています。
スポーンとなるパーティクルが徐々に減るようにParticles/[s]の値をアニメーションさせています。
最後は重力でパーティクルが落ちるようにしています。
この状態だとまだパーティクルがZのゼロより下にあってもパーティクルが出てしまいます。

■パーティクルがZのゼロ以下にあるときはパーティクルを出したくなかったのでPoint3ノードThresholdノードを使いパーティクルがZより上に移動したときにPosition Bornノードがオンになるようにしてみました。
一見問題ないのですが、(テストシーンがわかりづらくてすみませんでした。)
本番のシーンではこのつなげ方だと、パーティクルが徐々に減るというよりぷつりと切れて出なくなる印象になってしまいました。

■そこで色々試したのですが、うまく行かず、とりあえず回避策としてシードから発生するパーティクルがZのゼロ以下にあるときは消すようにしていました。見た目上は問題ありません。
ただし、計算上は一度スポーンのパーティクルを発生させてからポジション判定をして消していたのでデータの無駄が多くなっています。
今回パーティクルを大量に出したかったのでだいぶデータのロスになります。

■そこで解決策です。
種となるseedグループが水面より下にいる場合にはseed_Bグループにデータを送ります。たったこれだけでした…w
ここで重要なのはseed_Bグループを元のパーティクルseedグループの子供にしてあげることです。
このseed_Bseedグループの並列の場所に作るとうまく行きませんでした。

出来てしまえばなんてこと無かったのですが、今回は簡単なはずだけど、出来そうでできない。
後ちょっとで出来そうなのにうまく行かないというもどかしさに陥りました。
まだまだちょっとした事でつまずいているので、早くこのレベルから脱出したいものです。






2012年1月23日月曜日

ThinkingParticlesでPositionBornのOnインプットにノードをつなげたときの挙動の違いについて

今回はTipではなくちょっと困った問題が起きたため、問題解決のための質問記事です。
(自分のブログで質問ってのも変な感じですが…)

■TPでPositionBornのOnインプットにノードを繋げたとき、パーティクルの挙動がよくない結果になってしまいました。

作例は
①Teapotに元となるパーティクル(Seed)グループを発生させる
②SeedのパーティクルがZの0(ゼロ)より上に来たときにPositionBornでSpawnとなるパーティクルがを発生させたい
③なおかつ時間経過によってSpawnのパーティクルを減らしたい場合です。

検証ムービーと画像です。




ここでちょっと問題が起こりました。
右のように組めば、一応らしくはなるのですが、Spawnのパーティクルの減り方が思うように減ってくれません。
急にぷつりと減ってしまう感じです。
理想は左のパーティクルのような減って行き方をしてほしいのです。

では、SpawnがZの0(ゼロ)より下に来たときに消せばよいのではないか?と思うのですが、それだとパーティクルを大量に発生したときにどうしても重くなってしまいます。

もし、この問題の解決方法をご存知の方がいましたらよろしくお願いいたします。
もちろん引き続き検証をしていきますので解決したら、また記事にしたいと思います。

以下にmaxファイルを置いておきますのでご興味ある方はダウンロードしてみてください。(max2012)
TP_positionborn_test.max

2012年1月22日日曜日

ParticleFlowにあるSplitAmountをTPで再現する方法

ParticleFlowにはある一定の条件によってパーティクルを別のイベントに送るオペレータ「SplitAmount」がありますが、これに似たようなことをTPで実現する方法です。

まず、元となったのはsky-high-nest-blogのけゑさんのこの記事です。
(ほとんどそのままパクっただけですがw)
「(TP Basic) Randomオペレーターを使ったビューポートのパーティクルを間引く方法」
けゑさんはのブログはCGやってる人なら一度は見たことあるであろう有名なブログです、いつも大変勉強になる記事ばかりで、ホント頭が下がります。
この記事も自分が質問した事をものすごく丁寧に解説してくれました。
改めてありがとうございます!
自分もけゑさんのような丁寧な記事が書けるよう精進します。

では本題にもどります。
今回は以下の二つの方法を書きたいと思います。

元のパーティクルはシンプルでPositionBornでパーティクルを発生しredグループからblueグループに送っているだけです。
■Randamにある割合でグループを分けるには。
redグループからRandomオペレータ(NewValueParはAnimationを指定)をつないでThresholdで割合を決めてblueグループに送っているだけです
ちょっと謎なのがThresholdで割合を指定しても、Randomオペレータのシード値によっては思った割合にならないことがありました。

■パーティクルIDの範囲でグループを分けるには。
こちらはもっと簡単でredグループからThresholdで送りたいIDの範囲をしていてblueグループにつなぐだけです。

他の条件も再現できたらまた書いていきたいとおもいます。

TPをしばらく触った方なら、「知ってるよ。常識だよ」って記事だとおもいますが、元々このブログでのTPの記事は簡単なことや初心者の方に向けての記事ですので温かい目で見守ってくださいw
(自分のTPの経験がまだ乏しいだけですが…。)
もっといい方法あるよ。や他の条件を再現できるよって方はコメントなどいただけるとうれしいです。

2012年1月14日土曜日

ThinkingParticlesをつかってアニメーションオブジェクトを差し替えてみる。

TPを使ってインタラクティブにオブジェクトを差し替える作例です。

自分がTPを覚えようとおもったのはこういったことが効率的に出来ればいいなとおもったのがきっかけだったりします。
といっても難しいことは何も無いのですが、いくつか発見があり、忘れないようにしなければならないことがあったので記事にしました。

まずはムービーを見てください。


TP_AnimReplaceTest from take-imazu on Vimeo.


では解説です。

◆ Planeに配置されたパーティクルに対してアニメーションされたオブジェクトを割り当てています。
(配置しているオブジェクトはPlaneの脇にあるオブジェクトです)
Sphereが通り過ぎると(今回はDistansを使ってます)アニメーションが変わっているのが解るとおもいます。
これはアニメーションが変わったというよりオブジェクトそのものを差し替えています。
よく見ていただくと、BoxやTeapotのポリゴン数が変わっています。


◆まずはGenerateダイナミクスです。
ここはごく普通にPlaneオブジェクトのサーフェイスにPositionBornでパーティクルを配置しています。
(NodeヘルパーでPlaneオブジェクトを指定、SurfacePosヘルパーでFaceを指定)


◆次にShapeA_Assignダイナミクスです。
ここもごく普通にGeomInstanceノードでパーティクルに対してシーン内のオブジェクト割り当てているだけです。


◆次はShape-A_to_Bダイナミクスです。
ここではShapeAグループのパーティクルをSphereが近づくとグループBに送るようにしています。
(NodeヘルパーでSphereを指定、Distansで距離を指定)
今回はDistansノードを使用していますが、場合によってはInMeshノードでもいいと思います。
ここで大事なのはDistansノードでグループBに送ると同時にParticleDateノードでAgeを0にしてあげていることです。
(FrameヘルパーノードでAgeを0にしています。)

これはSphereオブジェクトが通り過ぎたときに差し替えたオブジェクトのアニメーションを0フレームからスタートさせたかったためです。
これが無いとグループBに送られた時のフレームでアニメーションを参照してしまい、アニメーションが途中から始まってしまいます。
今回ここをどう処理するか一番悩まされました。出来上がってみるととても単純でしたが…。

◆最後にShapeB_Assignダイナミクスです。
ここはShapeBグループに送られた後(Sphereオブジェクトが通り過ぎた後)のオブジェクトを割り当てなおしています。

◆ここで重要なのはShapeA_AssignダイナミクスのときGeomInstanceノードで割り当てたオブジェクトの順番と差し替えたいオブジェクトの指定する順番をそろえてあげることです。
ただし、そろえるだけでは駄目でGeomInstanceノードでShapeNumberとGroupノードをつなげてあげましょう。

オブジェクトの指定する順番をそろえなかったり、ShapeNumbeをつなげていないとオブジェクトが意図しないオブジェクトと指し代わってしまいます。

◆解説は以上です。
今回これをやりたかったのは、キャラクターが草むらを進むときに草を揺らしたりするアニメーションをテストしたかったのですが、草の倒れる方向や倒れるリアクションに自由度を持たせて自然に見せるにはもっと複雑な仕組みが必要そうです。

現時点の自分ではちょっと手に負えないかな…。
悔しいのでいずれ出来るようにがんばりたいとおもいます。

しかしヒキ絵だったり、たとえばキャラクターが歩いているときに足元で逃げる小さな虫とかに使う分にはそんなに難しく無さそうです。

ノードの詳しい解説などがもっときちんとできるようにもなっていければとおもいます。
解りづらい解説かとはおもいますが、少しでも何かのお役に立てれば幸いです。