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

2010年8月11日水曜日

ポリゴン・プレーンにシェーダーを市松模様にアサインする (1)

今回は仕事で、ポリゴン・プレーンにnClothを使って紙吹雪を作成した。
赤と白の紙吹雪を作るために色をつける必要があったのだが、当初、ポリゴンプレーンを市松模様にすればまんべんなく出来るのではないかと思いついた。
(実際にはその必要は全くなかったのだが)
そこで二つのシェーダー(赤と白)を各フェイスにアサインするのを自動でやってみようと思った。

目標:ポリゴンフェイスを選択して実行すれば自動的に赤と白のシェーダーを市松模様上にアサインするスクリプト

条件:30x30のポリゴンフェイスを持ったポリゴン・プレーンを使用


まずは先日、Twitterで@Atsushi氏に教えていただいたアドバイスを念頭におき、作業を進めることにした。

「物事をどれだけ単純な要素にバラせるか?」

細分化し、規則性を見つけ出せば、規則性のあるところはループさせることで自動化できる。


-------------
<一つ飛ばしのループ>
交互に違う色をアサインするためには、一つおきにフェイスを選択する必要がある。
まずは一つの色をアサインすることを考えてみる。

一つおきに何かが実行されるループを作ればよいと言うことなので、まずは一行分つくってみることにした。
プラス2(一つおき)の増分値をsubdivisionsWidth数に到達するまで繰り返す(15回繰り返す)ループを作ってみた。
printは仮のコマンドで、後に実行するコマンド群と置き換える予定である。

「細分化した単純な要素」として、まずはフェイスである。
フェイスにはそれぞれIDがあるので、最低限それがどのIDから始まるかを知っておけばFor文を作るときに役に立つ。
フェイスを選択し、スクリプトエディターの履歴表示で調べてみたところ
pPlane1.f[0] ではじまり、pPlane1.f[899] で終わっている。
要するにゼロから始まるループにする必要がある。

{
// color on a line
$width = 30; //subdivisionsWidth
$height = 30; //subdivisionsHeight
int $i;
for ($i=0; $i < $width; $i+=2)
{print ($i+",");};
}


結果: 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28


-------------
<市松模様にならない理由>
一見、これを最後のフェイスIDまで継続すれば良いようだが、そうすると、実は市松模様にならない。
上記のMelを最後のフェイスまで実行するとどうなるか少し実験してみた。

実際にフェイスを選択し、シェーダーをアサインしたときにスクリプトエディターの履歴に表示されるコマンド(「select」と「sets」)を使い、赤色をつけてみた。

{
// color on a line
$width = 30; //subdivisionsWidth
$height = 30; //subdivisionsHeight
$total = $width * $height;
int $i;

select -r pPlane1.f[$i] ;

for ($i=0; $i < $total; $i+=2)
{
select -add pPlane1.f[$i];
sets -e -forceElement lambert2SG;
};
}


しかし、結果は、このように市松模様にはならずにストライプになってしまう。




フェイス番号をつぶさに調べ、行が移り変わるときに何が起きているのかを調べてみた。

市松模様にフェイスを選択すると、選択されたフェイスの番号は以下のようになる。
一行目:0-29  (0,2,4,6,8,10,12,14,16,18,20,22,24,26,28)
二行目:30-59 (31,33,35,37,39,41,43,45,47,49,51,53,55,57,59)
三行目:60-89  (60,62,64,66,68,70,72,74,76,78,80,82,84,86,88)


ここにある規則性は
一行目:偶数フェイス
二行目:奇数フェイス
三行目:偶数フェイス

というふうに偶数と奇数が行によって交互に繰り返されるというパターンになっている。

色がつけられるフェイスを数字で見ていくと
まず一行目は、フェイスIDが29に到達すると次の行に移る、そのとき色がつけられるフェイスは28が最後であり、その次の行で色がつけられるのは、31からなので、色つけにFor文を使うならこのときだけ増分値は2+1=3である必要がある。
二行目の最後のフェイス59に到達すると次は60なので、このときだけ増分値は2-1=1にある。

一つのfor文では増分値は固定のため、これでは対処できない。
もう少し規則性を見つける必要がある。


-------------
<奇数行と偶数行の判定>
あらためて考え見ればフェイスIDの規則性は、複数の規則が合算されている。
それよりも、もっと単純な規則性を持っている部分は、奇数行と偶数行によって色がつけられるフェイスIDも奇数と偶数が切り替わっているという部分である。

奇数行と偶数行を判定できれば、条件分岐でそれぞれ違う実行内容を選択できるようになる。
パターンであるから必ず自動化できるはずである。

まず考えてみたのは、「subdivisionsHeight」の値を使えば、その判定ができるのではないかということ。

奇数か偶数かを調べるには剰余を求める演算子「%」を使い、余りを求め、それがゼロでなければ奇数と言うことになる。
ちなみにoddは奇数、evenは偶数のこと。

//odd or even
{
$height = 30;
int $j;

for ($j = 0; $j <$height; $j +=1){
$rem = $j%2;

if ($rem <1){
print ("even="+$rem+"\n");
} else{
print ("odd="+$rem+"\n");
};
};
}


これで行の判定はできるのだが、0~899まで継続するフェイスIDとの関連性を持たせるにはどうすればよいのかわからない。
行の判定はできるが、フェイスIDとは関連がないのだ。
(出来なくはないだろうが、これを書いている時点では明確に出来なかった。プログラミングでは明確でないものは書くことが出来ない)



わかっていることは
1)ループにフェイスIDを使う必要がある。(0~899)
2)奇数行と偶数行を見分けて、それぞれに違う実行文を使う。
(方法A(奇数行)を実行したら、方法B(偶数行)を実行する。)

ここでもう一度、考えを整理し直し。
もう一度パターンを見てみる。

1回目0-29(30)(偶数IDのみ)
2回目30-59(30)(奇数IDのみ)
3回目60-89(30)(偶数IDのみ)

これを見るとわかるが、繰り返しのパターンが現れている「30」という数字がそうである。

これでループの増分値は「30」であることがわかる。


次に同じ処理は何かを考えてみる。
1)一つ飛ばしで赤色をつける。
2)偶数行では、最初のマスから始める。
3)奇数行では、二番目のマスから始める。

奇数と偶数の処理が違うので、これは条件判定で切り替えることはわかる。
問題は、その条件である「奇数行」と「偶数行」を判別するにはどうすればよいかを考える必要がある。

増分値は「30」なので、これをもとにして奇数と偶数を振り分ける方法を考えてみる。
各行のフェイスIDは0,30,60,90・・・というふうに30を基準とした値で始まっている。
これを30で割れば0,1,2,3・・・という単純化され順番をしめした数字になる。
それに対して演算子「%」を使えば各行が奇数か偶数かは判定出来るはず。

00/30=0 -> 0%2=0
30/30=1 -> 1%2=1
60/30=2 -> 2%2=0
90/30=3 -> 1%2=1
・・・


これを条件判定に使えば、奇数行と偶数行で異なるコマンドを実行できる。

これを実際に使ってみたのが以下の物。

{
$width = 30; //subdivisionsWidth
$height = 30; //subdivisionsHeight
$total = $width *$height;
int $i;

for($i=0; $i<$total; $i+=$width){
$odd=$i/2%2;
if ($odd==1){
print ($i+" =odd line "+$odd+"\n");
}else{
print ($i+" =even line "+$odd+"\n");
};
};
}


現在の行が奇数行か偶数行かを判定し、それにより実行内容を交互に切り替えている。
また$iの値は各行の最初のマス目のfaceのid番号を示している。
要するに最初の「For」文で使われている変数「$i」の値をそのまま「if」文の中で使うことができ、各行のループの初期値として利用できると言うことである。

これで基本となる流れの部分はできた。
あとは奇数行と偶数行で行われる内容を記述してやればよいことになる。
(つづく)

1 件のコメント:

  1. 呼ばれた気がするのでコメント残しておきます(笑)
    つづきがあるっぽいので、今つっこむのは野暮かもしれませんが(^^;


    さて、このアプローチですが、着眼点はいいと思います。
    ただ、ちょっと惜しいんですよね。
    せっかく行単位に処理をバラしたのですから、もう一歩踏みこんで、プレーン一個ずつの単位までバラすとよかったです。


    各プレーンの情報は、列($x)・行($y)・プレーン番号($id)のみっつだけです。
    で、これらをうまく使えば、簡単にシェーダーを適用するプレーンを決定できるんです。

    余談ですが、$x, $y, $id の関係は以下の式で表せます。

    $x = $id % $width;
    $y = $id / $width;
    $id = $y * $width + $x;

    これは画像処理とかで多用する式なので、丸暗記でいいかもしれません。


    ちょっと話が逸れました。

    本題としては、どこにシェーダーを適用するかですよね。
    ものは試し。各プレーンで $x と $y を足してみてください。
    法則性が見えてきませんか?

    ちなみに、少しひねると25%や75%の網掛けも簡単に実現できます。
    ヒントはかけ算。


    世の中、意外なほどに単純です。

    返信削除