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

2009年5月22日金曜日

エクササイズ: ロケータ位置を頂点としてポリゴンを作成する。 (2)

前回の「エクササイズ: ロケータ位置を頂点としてポリゴンを作成する。 (1)」の続き。


前回までで、ロケータとポリゴン頂点のトランスレートをコネクトできるとわかったので、今回はその微調整の手順を検証することから始めたい。

ポリゴン頂点を「polyCreateFacet」を使って作成した場合、全ての点が「0 0 0」の頂点座標を持つことがわかった。

これは「ポリゴン作成ツール」で作成したポリゴンの頂点を選択し、チャンネル・ボックスに表示されるCVをクリックすれば表示されるので容易に確認できる。


頂点をこのまま各ロケーターのトランスレートにコネクトすると、
現在のロケーターの座標が現在の頂点位置を原点として加算されるため、位置がずれてしまう。

わからない人は、以下の手順をやって確認してみて欲しい。
1)ロケータを一つ作成。
2)ポリゴン作成ツールで三角形を作成。
3)ロケータのトランスレートと、作成したポリゴンの一つの頂点をコネクションエディターで接続する。
(詳細は前回のエントリを参照



その微調整のためには、

●コネクトする際に値をオフセット補正(+-)する。
●ポリゴン作成時に頂点をまず0 0 0へ作成し、その後ロケータ位置へ移動する。

の二つの方法がある。



----------------------------------------
しかし、コネクトする際に値をオフセットする方法は簡単そうだが、アニメーションが進むにつれて誤差がでてくるような気がする。

別にMayaの中で行われる計算についてはっきりとした裏付けがあって言っているわけではなく、

以前、二つのオブジェクトの位置の誤差を電卓で計算して、チャンネルボックスでマイナスして入力したが、二つのオブジェクトの位置が微妙に合わないという状況を経験したことがあるので、あまりMayaの計算を信用していないということ。

またもう一つの理由はコンピュータ全体に言えることだが、「浮動小数点の誤差」の問題だ。

これはコンピュータが二進数で計算するために、比較的単純な計算でさえ誤差が生じることがある。 これは「浮動小数点数の誤差」として知られている。


これはプログラミングだけでなくコンピュータをつかった計算では比較的知られた問題で、以下のサイトを見てもらえればそれがわかるだろう。

学びの場.comにある渡辺俊雄氏の誤差とどう向き合うか」。 とてもわかりやすい。

unibonさんのサイトの演算誤差について(10進数と2進数)も参考になるだろう。

アンクル・トムさんによるパソコンとITの雑学ブログコンピュータの計算誤差

ITproの「コンピュータにおける「データ表現」の基礎(第5回)には「誤差」の種類別に解説がある。



ここに書かれているように人間の頭では十分計算可能な、単純な物でも二進数をつかった計算では誤差が生じてしまう。

そしてそれはMayaも例外ではない。
ためしに上記「誤差とどう向き合うか」の例を参考にしてスクリプトエディターで試してみた。
そしていろいろ試した結果、誤差がでてくるのは20桁以後ということがわかった。
ぜひ試してみて欲しい。translateで「4」もずれれば結構大きな差になることがある。


19桁
print (1111111111111111115-1111111111111111111);
答え:4

20桁
print (11111111111111111115-11111111111111111111);
答え:0

このような数字をそのまま使うわけではないので、そのままMayaの座標計算で影響が出るかどうかはやってみないとわからないが、それを試す時間がもったいない。


それから、この結果はコンピュータの構成によって異なる可能性があるので(特に32ビットと64ビットでは違う可能性大)うちの構成を参考までに下に書いておきます。
OS:Windows XP sp2 32bit
CPU:Athron MP (x86)



--------------------
そこで二つ目の方法、「頂点を原点に作成し、ロケータの位置へ移動する」だが、よく考えたら非常に簡単
各頂点作成後、頂点をロケータへコネクトすれば頂点位置は勝手にロケータ位置へ動いてくれる。


これで想定できる問題はほぼ片付いたので部品の組み立てに入りたい。

もう一度流れを見てみると
0)ロケータを選択する。
1)ロケータの位置情報を得る。
2)頂点を作成してから、頂点をロケータ位置へ移動する。
3)上記を繰り返しポリゴンを作成。
4)ロケータの位置が移動したとき、ポリゴンの各頂点も更新されるようにする。
この1~4はループ内で行い、ひとつのロケータ、ひとつの頂点ごとにステップを踏む必要がある。


少しづつ確認しながらやっていると以下の時点でエラーがでた。
{

string $obj[] = `ls -sl`; //選択した全ロケータの名前を配列に格納。
int $total = size($obj); //for文で使うため、ロケータの総数を得る。

for ($i=0; $i<$total; $i++){

vector $L_position = `xform -q -ws -t $obj[$i]`; //ロケータの座標を取得
polyCreateFacet -p $L_position;

}



// エラー: 引数の解析エラーです。 //

$L_positionのところを 0 0 0にすると

// エラー: ポイント数が不足しています: 最低 3 つが必要です。 //
というエラーに変わる,これはエラーではあるが数値としての0 0 0は認識していると言うことだ。

ためしに
{
$x=1;
$y=1;
$z=0;
polyCreateFacet -p $x $y $z;
}

と入力すると、
// エラー: ポイント数が不足しています: 最低 3 つが必要です。 //
と同じエラーなので変数が使えないわけではないらしい。
ただ、vector形式を使うことはできないらしい。


それに今気がついたがpolyCreateFacetでは最低3つのポイントがないと作れないので、for文と組み合わせて頂点を一つづつ作ることは出来ない。
それに後からポイントを追加できるのかどうかもわからない。(おそらく違うコマンドが必要)
新たな壁が...。


ならまずすべてを 0 0 0の位置に作成してからコネクトすればどうか?
どうせ順番はかわらないし、コネクトした時点で頂点の位置はロケータの位置と同じになるはず。



--------------------
これでいけるかもしれない。

アルゴリズムを書き直してみた。わかりにくかった説明もついでに修正。
0)ロケータを選択する。
1)ロケータの数だけ、0 0 0へ頂点を作成する。(polyCreateFacet -p)
2)頂点とロケータのトランスレートをコネクトする。(connectAttr)


これだとロケータの位置情報をいちいち取得する必要もなくすっきりまとまりそうだ。
気を取り直して作業再開。


--------------------
よく考えてみると、connectAttrの使い方を調べてなかった。
オンラインヘルプの例をみると以下のような形式で接続できる。

connectAttr 1stObjectName.Attr 2ndObjectName.Attr ;

それに、translateをコネクトするのだが、前回のテストのようにxyzを別々にするのではなく、translate全体を一度にコネクトできるかどうか試してみたい。

まず
locator1
polySurface1.vtx[1]
の接続を考えてみる。

前回、調べた頂点のアトリビュート名を引用しておく。
pnts[0]
pnts[0].pntx
pnts[0].pnty
pnts[0].pntz


最初の「pnts[n]」は、ポイントのアトリビュート名であると共にトランスレートを示しているのだと思う。
そしてそれをさらに細分化したのがpntx,y,zだろう。
なのでロケータのtranslateと頂点のpntsを接続すればよいはずだ。

アトリビュート名をつけて記述しなおすと接続するアトリビュート名はそれぞれ
locator1.translate
polySurface1.pnts[1]
ということでよいだろうか、

コマンドにすると以下のようになる。
connectAttr locator1.translate polySurface1.pnts[1]

成功!


さて、ここで注意すべき点がひとつあった。
めんどくさいのでここでは追求しないが、ポリゴンの名前だ。

コンポーネントはpolySurface1Shape(シェイプノード)に含まているはずだったのだが、polySurface1(トランスフォームノード)で問題ないようだ。


さて作業開始。とりあえず最後まで書いてみた。

テスト方法は、以下の通り。
1)ロケータを三つ以上作成しばらばらになるように任意の位置へ移動する。
2)ロケータを順番に選び、下記のスクリプトを実行する。
3)ポリゴンが作成されたらロケータを動かしてみて、各頂点がそれぞれのロケータについて動くようなら成功。



{
string $point;
string $polyName;

string $obj[] = `ls -sl`; //選択した全ロケータの名前を配列に格納。
int $total = size($obj); //for文で使うため、ロケータの総数を得る。

// すべてのロケータと同じ数、-p 0 0 0 を繰り返す。(同じ頂点数)
for ($i=0; $i<$total; $i++){

if ($i==0) {
$point = " -p 0 0 0 ";
}

else {
$point =$point + $point;
}
}

eval ("polyCreateFacet" + $point + ";"); // ポリゴンオブジェクトを作成。
$PolyName = `ls -sl`;//今作成されたポリゴンのノード名を取得。

for ($i=0; $i<$total; $i++){

 //ロケータのアトリビュート名まで変数へ格納
string $loc_name = $obj[$i] + ".translate ";

 //ポリゴンのアトリビュート名までを変数へ格納
string $vtx_name = $polyName + ".pnts[" + $i + "]";

connectAttr $loc_name $vtx_name;
}
}


うまく動いたが、頂点が一つ多く0 0 0に作成されている。
もしかしたらこれは先日の「exercise: ロケータ位置をpvとするカーブを描く」の時と同じ間違い??
そうすると頂点一つどころかたくさんの頂点が0 0 0へ存在していることになる。

前回うまくいったループを見直してみる。
for($i=0; $i<$total; $i++){
$pObj = `xform -q -t -ws $name[$i]`;
$point = $point + " -p " +$pObj;
}


今回うまくいかなかったのはこれ
for ($i=0; $i<$total; $i++){

if ($i==0) {
$point = " -p 0 0 0 ";
}

else {
$point =$point + $point;
}
}



変数名が違うのとif文は使っていなかった。
どうもif文を安易に使ってしまう傾向がある。
よく考えたら間違っているのだが、作っている最中はこれで間違いないと思っている。

for ($i=0; $i<$total; $i++){
$point0 = " -p 0 0 0 ";
$point =$point + $point0;
}

ポイント:
1)追加していく値だけを最初の文で作成
2)次の文では左辺の変数名と同じものを右辺へ入れそれに先ほどの値の入った変数を追加してやる。

最初の時点で右辺にある$pointには値がないので-p 0 0 0が$pointに入っている。
ループ二回目で$pointの値(-p 0 0 0)に$point0の値(-p 0 0 0)が追加され $pointの値は(-p 0 0 0 -p 0 0 0)となる。
ループ三回目では、$pointの値(-p 0 0 0 -p 0 0 0)に$point0の値(-p 0 0 0)が追加され $pointの値は(-p 0 0 0 -p 0 0 0 -p 0 0 0)となる。


これでうまくいきそうだ。
もう一回、新しいループの部品を含めて修正してみた。

{
string $point;
string $polyName;

string $obj[] = `ls -sl`;
int $total = size($obj);for ($i=0; $i<$total; $i++){
$point0 = " -p 0 0 0 ";
$point =$point + $point0;
}

eval ("polyCreateFacet" + $point + ";");
$PolyName = `ls -sl`;

for ($i=0; $i<$total; $i++){
string $loc_name = $obj[$i] + ".translate ";
string $vtx_name = $polyName + ".pnts[" + $i + "]";
connectAttr $loc_name $vtx_name;
}

}


これでうまくいった。大成功!


--------------------
今後の課題:
ロケータを選択するとき線が交差しないように順番に選んでやる必要がある。
たとえばロケータが輪になっているとすると、
一つのロケータから始め、そのとなり、そして次はそのとなりという順番に選ぶ必要がある。
飛び飛びに選んではいけない。

そのためロケータの選択時には上記の点に気をつけて慎重にやらなくてはいけない。
選ぶ順番が違うと不自然に頂点のつながりが飛んでしまい、ポリゴン面が交差してしまう。

無作為に選択しても、これを解決することができれば、よいのだが。

 
また今回はトータルでほぼ一日がかりの作業時間となっている。
実際の現場でこのスクリプトにこれだけの時間を掛けるだけのメリットがあるのかどうか?
たしかにこの方法でしかうまくいかないのなら時間を掛ける必要があるが、
何度も使うわけではなく一度しかつかわないなら、ポリゴン作成ツールとコネクションエディターを使えば長くても1時間もかからない。

このようなスクリプトなら1時間程度で作れなくては実用的とは言えないということか。

まだまだ勉強が必要だなとひしひしと感じる。

 

0 件のコメント:

コメントを投稿