日本でハリウッドVFXを制作! 「経産省アイディアボックス」 結果:  
●まとめエントリはこちら ●FAQ ●お問い合わせは左のメールフォームから

2009年5月18日月曜日

ゴールに近づいたパーティクルの色を変える (2)

結果から言うと今回も惨敗。
詳しいことを知りたい人は読んでも良いが、ちゃんと動作する結果だけが知りたい人は、長いので読むだけ時間の無駄になる。

--------------------
いよいよオンラインヘルプにあった
「たとえば、ゴール オブジェクトに近づくにしたがってカラーが濃くなるように、パーティクル カラーを変更することができます。 」
を実現するエクスプレッションを作成してみたい。

仕事では、パーティクルを使うことが多いため、これができれば少し自信につながる。


現時点の自分の知識で考えるとこのエクスプレッションの流れは以下のようになる。
1)個々パーティクルの位置をIDを使って取得
2)その値からターゲットオブジェクトとの距離をmagコマンド割り出す。
3)その値を使って個々のパーティクルの色を修正する。

こんな回りくどいことをしなくてもいいのかもしれないが、わからないので仕方がない。

この方法で、ひとつひっかかるのは、
エミッターからパーティクルを発生させる場合、パーティクルの総計はどんどん増加する。
そのため、個々のパーティクルを一つ一つ計算するこのやり方では、時間が経過するに従って計算量が増え、非常に重くなるのではないかと言うことだ。
おそらく、重くなることは避けられないことなので、エミットの上限を決めておいた方が良いだろう。

とりあえず、この方法でやってみることにする。
ステップ1はgetAttrかそれに類似した方法が使えるだろうが、調査が必要。
ステップ2はすでに知っている方法で問題ない。
ステップ3はperParticleAttributeにrgbPPを追加し、パーティクルID毎に、その値を変化させることで対応できるだろう。



まず、ステップ1については、Digital Matrix(Mel Tips)のダイナミクスの項目にパーティクルの個々のアトリビュートを調べる方法が書かれているのを発見。

方法は二つ。
1)particleコマンドに-queryフラグを使って取得する方法
particle -attribute position -id 2 -q particle1;

2)getParticleAttrコマンドで取得する方法
getParticleAttr -at position -array true particleShape1.pt[1];

基本的にどちらも個々のパーティクルのアトリビュートをIDを元に調べる手法でどちらも返り値は、ベクトル形式だ。

ただ、getParticleAttrはワイルドカード「*」が使用可能だ。
getParticleAttr -at position -array true particleShape1.pt["*"];


--------------------
まずIDを使う方法だが、当然一つ一つのIDを指定することはできないので、for文でループを作り
ID[0]からその時点での最後のIDまでの数字を指定してやる必要がある。


そのためには、
A)パーティクルの総数($total)を取得
B)for($i=0; $i=$total; $i++){}でループを作成(パーティクルIDはゼロから始まる)
C)particle -queryかgetParticleAttrコマンドを使用する。

この1~3はgetParticleAttrでワイルドカードを使用すれば省略できる。
しかしながらそうなると、値がすべて連続した物として帰ってくるので、ひとつひとつの値を分けて使うための手法が必要になる。

現在の技術で、for文を使ってそれを作ることも出来るが、パーティクルIDをfor文で取得する方法よりも手間がかかるし頭を使わないと行けないので、今回は見送った。


まず、particle -query を使った方法を試してみることにした。
ステップA:パーティクル総数は、particleノードの読取り専用アトリビュート「count」に計上されているので、これを取得すればよい。
$total = `getAttr particle1.count`;


ステップB:$totalの数だけ繰り返せばいいのだが、IDはゼロから始まるので$total数よりは一つ少ない数で終わる必要がある。
for($i=0; $i<$total; $i++){}

ステップC:
基本的にはDigitalMatrixに照会されていたとおりでよいが
particle -attribute position -id 2 -q particle1;

forループによってidを変化させる必要があるので、まず全体を文字列として扱い、$total変数を「文字列の連結」として扱う必要がある。
vector $tPPvec eval("particle -attribute position -id "+ $i+ " -q particle1;")

evalコマンドは遅くなると聞いていたので、不安は残るがこれしかわからないので仕方がない。
DigitalMatrixにもevalの説明に、

文字列をコマンドとして実行
eval string
eval arg1 [arg2 [...[argn]]]
実行時まで決定できないコマンドを実行する場合に使用
と書いてあるので、大きく間違ってはいないだろう。


一つにまとめてテストしてみた。

{
$total = `getAttr particle1.count`;
for($i=0; $i<$total; $i++){
vector $PPvec = eval("particle -attribute position -id "+ $i+ " -q particle1;");
print $PPvec;
}
}

ちゃんとベクトルで値が返ってきたので間違いはなさそうだ。


--------------------
つぎに全体の流れのステップ3「その値を使って個々のパーティクルの色を修正する。」
とうところを作ることにする。

個々のパーティクルの色を作るにはrgbPPを使うのでまずperParticleAttributeでrgbPPを作成。

rgbPPのデータ型はvectorもしくはarrayと言うことになっている。(参照:オンラインヘルプ

そしてベクトルの値をrgbPPへ代入する記載方法については、オンラインヘルプをもう一度みてみた。

ベクトルのリテラル表現は、3 つの浮動小数点数をカンマで区切って << と >> で囲みます。
vector $roger = <<3.0,>>;
とあるのでこの形式で代入してやる必要がある。

前回、色の変化はlinstepと$dist(距離)によって計算した。
それが以下の物で、$colorは赤で、$color2はBGを示す。
$color = (1-$result/2);
$color2 = ($result/2);

ベクトル表現では<>になるので
rgbPP = <<$color,$color2,$color2>>;
ということでよいだろうか?
結果を見るにはとりあえずエクスプレッションを作ってみるしかない。


--------------------
前回までのスクリプトをベースに今回作った部品を組み合わせてみる。

//per particle position
int $total = `getAttr particle1.count`;
vector $PPvec;
for($i=0; $i<$total; $i++){
vector $PPvec = eval("particle -attribute position -id "+ $i+ " -q particle1;");
}

//goal position
string $goalName = "nurbsSphere1";
vector $obj1Vec = `xform -q -t -ws $goalName`;

vector $distVec = $obj1Vec - $PPvec;
float $dist = `mag $distVec`;

//linstep caliculation
$result=`linstep 0 2 $dist`;

//get color value.
$color = (1-$result/2);
$color2 = ($result/2);
particleShape1.rgbPP = <<$color,$color2,$color2>>;

//show current distance.
print ($dist + "\n");


しかしながら、数が80個を超えるあたりから非常に重くなってきたのでパーティクルの最大数を10に設定
linstepの最小値を0へ、最大数を11から2へ変更
runTimeBeforeDynamicsでエクスプレッションを作成した。

実行した結果。
// エラー: 指定された ID のパーティクルが見つかりませんでした。照会コマンドの ID の値を確認してください //
// エラー: ダイナミクス エクスプレッション前のランタイムで実行エラーが発生しました particleShape1. //

が繰り返され、パーティクルの色が変わらない。
正確には色は変わるのだが、発生時にゴールとなるスフィアの距離に応じて色が決定され、以後更新されない。
それはスクリプトエディターをみていると、わかるのだが、current distanceの表示が最初の発生時に表示されるだけで後は更新されない。


切り分けのために各パーティクルの値を取得するところだけにしてそれを表示するようにしてみた。

//per particle position
int $total = `getAttr particle1.count`;
vector $PPvec;
for($i=0; $i<$total; $i++){
vector $PPvec = eval("particle -attribute position -id "+ $i+ " -q particle1;");
}
//show current distance.
print ($PPvec + "\n");

が、まだエラーは出る。
問題がないと思っていたところだけに、ショックは大きい。
こういうことが重なるとMelの勉強を続ける意欲が削がれてしまう。

とりあえずスクリプトエディターで分析してみる。
5フレームすすめたところで最初のエラーが出始める。

それ以前は、座標が表示されている。(合計12行)
すべて同じ値が表示されているので、発生時の位置だろうか?


座標の値は
6.9676929e+252 1.693382427e-152 9.997653658e+251

原点でもないらしいので、これがどの位置になるのか、オブジェクトを一つ作成し、この値をそれぞれtx,ty,tzへ入力してみた。
視界からオブジェクトが消えてしまったのでフレーム内表示のためにfキーをおしたところ何も表示されなくなった。あわてて新しいオブジェクトを作成し、fキーを押すが画面内に表示されない。
ビューの問題かと思い、ビューを変えても同じ現象が再発。
どやらマジックナンバーに当たったらしい。


気を取り直し、新規シーンにして、エミッタとゴールオブジェクトを作り直し。
ちょっと気になったので、今回は、パーティクル総数を設定せず、エミッターの放出量にキーフレームを売って、量をコントロールするようにした。
すると数字は変わらないが、エラーは無くなった。
再度パーティクル総数を設定するとまたエラーが発生するようになった。

これはMayaのバグなのか?それとも使い方がわるいのか??
とりあえず、そのあたりを考えても仕方がないのでエミッターの放出量にキーフレームを打つ方法を使うことにした。


エラーはでなくなったが座標値の値が変化しないのでは意味が無い。
パーティクルを一つだけひろってみてその座標値が変化するかどうか見てみることにする。

//per particle position
vector $PPvec = `particle -attribute position -id 1 -q particle1`;
//show current distance.
print ($PPvec + "\n");


これを実行してみると数値はちゃんと更新される。
ということはevalコマンドがまずかったのか?
思い切って以下の方法を試してみると、
//per particle position
int $total = `getAttr particle1.count`;
vector $PPvec;
for($i=0; $i<$total; $i++){
vector $PPvec = `particle -attribute position -id $i -q particle1`;
}
//show current distance.
print ($PPvec + "\n");

evalでなくてもエラー無く実行できた。
ただ数字は更新されない。
ということは問題はfor文にあるのか?
for文をじっくり見直したが問題になりそうな部分は見つからない。文法的には問題はなさそうだ。
あるとすれば$totral。
一番最初の行で、$totalはちゃんとした値を得られているのか?

エクスプレッションを以下のようにして調べてみた。

int $total = `getAttr particle1.count`;
print ($total + "\n");

アトリビュートエディターで観察した結果は以下の通り
フレーム1で0
フレーム2で3
フレーム3で5
フレーム4で7
フレーム5で8
以後8のまま

スクリプトの返してくる値は
フレーム1で0
フレーム2で0
フレーム3で3 (が3つ)
フレーム4で5(が5つ)
フレーム5で7(が7つ)
フレーム6で8(が8つ)
以後同じ。

という形になっている。
そのときカウントされた値の総数だけ返り値が繰り返し表示されている。

一フレーム毎に上記のコマンドをスクリプトエディター内で実行するとちゃんとした値を返してくる。


これをクリエイションエクスプレッションにすると

フレーム1で0
フレーム2で3(が3つ)
フレーム3で5 (が2つ)
フレーム4で7(が2つ)
フレーム5で8(が1つ)
ちゃんと発生時に計算されているが、返ってくる値は発生時のパーティクルの数だけ返ってきている。

どうやらパーティクルの数だけ、この式の評価は繰り返されているらしい。


ここでfor文の方も確認してみることにした。
$i<8
にして実行してみたところ、最初の二回は値が返ってこず、そのあと3回エラーになり、それ以後、値が返ってくるようになる。


どうもスクリプトでは問題ないが、エクスプレッション独自の書き方に従う必要があるのかもしれないが、現時点ではわからないので、暗礁に乗り上げた形となった。

 

0 件のコメント:

コメントを投稿