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

2010年8月14日土曜日

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

まずは前回までの内容を簡単におさらいします。

まず自分が、これでいけるのではないかという処理の仕方を考え、全体の流れを作った。

簡単にまとめると、既知の情報、今回のスクリプトでは、
フェイスID(0-899)、subdividsionsWidth 30
の二つを使って、条件(奇数行か複数行か)を見分けて、
それぞれ別の処理を条件にしたがって行える流れを作った。

また、このエントリでは、@Atsushiさんからもコメントをいただき、非常に有益な法則をおしえていただきました。是非前回のエントリのコメント欄をご覧ください。
現在はその法則に関する、理解を深めているところですが、消化できしだい、その方法を使ったスクリプトをつくってみたいと思っています。

今回はとりあえず、前回の段階で頭の中で考えていた物を出してみました。


-------------
<一つおきに彩色するスクリプト>
今回は、実際に実行する部分のスクリプトを作っていきます。

実際には「ある行で、二つおきにポリゴンに彩色する」スクリプトである。

これは前回、作りました。
前回のエントリで2つ目に上げたスクリプトを、少し修正した物を以下に再掲します。

※ 前回書き忘れましたが、このスクリプトはプレーンの名前は「pPlane1」、シェーダーグループ名は「lambert2SG」(シェーダーは赤などの色にしておくとわかりやすい)でなくては動きません。
<修正前>
{
// 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;
};
}


1)ループの始まる前にフェイス「0」を選択していましたが、$iの変数にゼロを代入していなかったので代入。
2)そもそも、ここで選択しなくてもループで選択されるのでこの行自体を削除 (最初はselect -addだけでは最初のものは選択できないと思っていた)
3)select -rがなくなったので、何か選択していたら、それが残ってしまう。(たとえばオブジェクトを選択していたらそのオブジェクトにも色がついてしまう。)のでループが始まる前に、selct -clを追加。
4)ループ終了後に選択を解除するためにselect -clを追加。
5)今回は1行分ができればよいのでループの条件に、$totalを使わず、$widthを使った。

<修正後>
{
// color on a odd line
$width = 30; //subdivisionsWidth
$height = 30; //subdivisionsHeight
$total = $width * $height;
int $i=0;

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


これを実行すると最初の1行だけ、一つおきに色をつけることができます。
例: 一行目:0-29  (0,2,4,6,8,10,12,14,16,18,20,22,24,26,28)
これを奇数行につかうことにします。


-------------
<偶数行で彩色するスクリプト>
つぎに偶数行です。
偶数行への色づけは最初のフェイスが一桁目が「1」のものから色をつけていきます。
例: 二行目:30-59 (31,33,35,37,39,41,43,45,47,49,51,53,55,57,59)

上記のスクリプトに少し手を入れてこれを実行できるようにします。
二行目用なので、
1)30から始まるので$iの初期値を30にする。
2)しかし、実際の色づけは31からなので「+1」を$iの初期値に加えています。
3)条件も30+$widthの値にする。

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

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


これを実行すると二行目に色がつきます。


-------------
<ポリゴン・プレーンを市松模様にする>
さてこれを前回最後に作ったスクリプトに組み合わせてみる。
予定としては
1)$iはフェイスIDとして使う。
2)メインループは、行のカウントのみに使う。
3)サブループはある1行のなかの処理(フェイスの着色処理)のみに使う。
4)メインループの$iの値をサブループで使うことで、行の情報を利用することが出来る。
5)メインループへ戻ってくるのは1行の処理が終了した後、すなわち$widthが増分値となる。


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

for($i=0; $i<$total; $i+=$width){
$odd=$i/2%2;  // odd=0 mean the line coloring start from odd number , not mean odd line.

if ($odd==1){
    // color on an even line
    select -cl;
        for ($e=$i+1; $e < $i+$width; $e+=2)
        {
        select -add pPlane1.f[$e];
        sets -e -forceElement lambert2SG;
        };
    select -cl;
    }else{
    // color on a odd line
    select -cl;
    for ($o=$i; $o < $i+$width; $o+=2)
        {
        select -add pPlane1.f[$o];
        sets -e -forceElement lambert2SG;
        };
    select -cl;
    };

};
}


これで無事、市松模様になりました。



ただ安心するのはまだ早い。
WidthとHeightの値が異なる数字でもちゃんと機能するかどうか? ポリ・プレーンのサブディビジョンとスクリプトの変数を変えて試してみることにします。
スクリプトは「汎用性」があるほど、有益ですから、これは自分のスクリプトの価値を試す重要なテストです。


....。

結論から言うと、試すんじゃなかった。

width = 20
height = 30

にすると、一列に並んで縞模様になってしまいました。
$odd=$i/2%2;
全部偶数と判断されるようです。

width = 19
height = 30

にすると、こんどは2行おきに市松模様。
同じく、$odd=$i/2%2;
の行で、奇数行、偶数行の判定がうまく出来ていないようです。
まあなんで最初2で割る必要があるのか? これは、Widthが偶数でも2で割れば奇数にできると思ったからなんですが、それが言えるのは10とか30といった数字だけの話でした。
20とか40、そして19には使えません。


どうも汎用性がある奇数行、偶数行の判定はこの公式では駄目そうですね。


数字の基礎的な部分の法則について、知らなかったことが原因の一つでしょうね。
あまりにも、無知すぎますが、まぁこういった失敗を繰り返すことで、知識がしっかりとした物になるのだと思います。

数学の法則を見つけていくのは大変な労力が必要だし、全て見つけるのは不可能ですから、@Atsushiさんのような経験者の方からいただくアドバイスは非常に、良い手助けとなります。

しかし、アドバイスいただいたことを、しっかりと理解し、自分の中で消化し、それを身につけることも同じぐらい重要なことなので、じっくりと検証していきたいと思います。

しかし、「市松模様」 思った以上にはまりました...。
(つづく)


-------------
奇数と偶数判定のMelについてざっと調べてみたところ、情報がいくつかあったので次回のためにメモ:
1) 林田ブログ 「楽しんでスクリプト
2) Digital Matrix 「tripleSwitch
3)Area 「mel script with diamond checker board question tia sal2
4)SPAFi 「Even or Odd? Black or White?


-------------

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

4 件のコメント:

  1. なんか成り行きで片足つっこんでしまったので、完成までおつきあいしますね。

    私は立場上、行き詰まってるプログラマやデザイナーに、解決方法を示唆する必要があります。
    そして、この記事のように順を追って書いていただいてると、どこで詰まったのか見えてきやすいです。
    そういう意味では、私も勉強させてもらってます。


    さて、今回の記事で気づいた点がふたつ。

    1) プログラムは総当たりで単純化
    2) プログラムは欲張ると複雑化

    この二点を意識すると、スクリプトの理解がしやすくなると思います。


    1) プログラムは総当たりがもっとも単純

    このプログラムでは、for文で条件に一致するフェイスを抽出しようとしてますね。
    でも、こういうのはプログラム的にはあまり推奨できません。ややこしいですから。
    まずは全部のフェイスを順次処理して、条件に合致する場合だけシェーダーを適用するアプローチにします。
    できるだけ、for文で(;;$i+=2)みたいなことはせず、(;;$i++)にしましょう。
    一見、総当たりは無駄が多そうですが、実際は総当たりのほうが高速な場合が多いです。
    ましてや速度を求められるゲームプログラムならいざしらず、melですからね……人間が楽をできれば十分です。

    for (総当たりループ) {
    if (条件に合致するフェイスなら) { シェーダー適用 }
    else { 何もしない }
    }


    2) プログラムは欲張ると複雑化

    「$iはフェイスIDとして使う」としてますが、最初のforループでやろうとしていることは行のループです。
    「$odd=$i/2%2;」でも行の偶奇を判定しようとしてますよね。
    こういうときは、面倒でも必ず行を表す変数を用意します。
    欲張ってひとつの変数で複数の意味を持たせようとするとごっちゃになります。
    $line =$i/$width;
    $odd=$line/2%2;
    としてれば、今回のプログラムでも正しく動作するはずだったんです。
    もっとも、本当の正解は「$iは行の番号(0からスタート)として使う」なんですが。

    for($i=0;$i<$heigt;$i++)
    { フェイスIDの開始数は$width*$iで求められる }


    なんかネタバレしないようなヒントを意識したら、かえって分かりづらくなってしまったかもしれません(^^;
    毎度の長文で失礼しました。

    返信削除
  2. 長文大歓迎ですw。
    またまたすばらしいアドバイスをありがとうございます。「プログラムは総当たりがもっとも単純」「 プログラムは欲張ると複雑化」最初は意味がわかりませんでしたが,例を読んでいるとよくわかりました。
    どちらも最初のアドバイス「細分化」に深く関係しており、「単純化」がキーワードですね。
    おそらくややこしくなるのは、まだ考えがまとまりきってない段階で、何か作ろうとしてああだこうだと練っているからのもあるんだと思います。
    ご説明をよんでいると、頭の中で様々なパターンを明確に認識した上で物事を進めているんだなということがよくわかります。
    実際、私は考えをまとめるのが下手なので、ブログに書いている時点でも、まとめきっていませんw
    これももっと勉強が必要ですね。

    返信削除
  3. あ、偉そうなコト書いてたのにミスしてる(^^;

    誤「$odd=$line/2%2;」
    正「$odd=$line%2;」

    ですね。スミマセン。
    今手元にMayaがないのでチェックできてませんでした……と苦しいイイワケw

    ちなみに、私も考えてる途中はけっこうぐちゃぐちゃになってます。
    でも、あまりこんがらがってくると「めんどくせー! やってられっかーーー!」となってしまうので、あらかじめ単純な形にする癖をつけてるんです。
    途中で投げださないためのコツですねw

    返信削除
  4. 最初ちらっと、もしかしてと感じたんですが、充分検証しておらず、あれこれでいいのかもと思っていました。
    やっぱり間違いでしたかwww
    わざわざありがとうございます!!

    経験者の方でも、考えている途中はぐちゃぐちゃしてくることはあるんですね。
    そういえば、ちょっと違いますが車の修理でも製図とか木工でも、仕事でもそうですが、熟練者ほどできるだけ物事を単純化し、ステップを明確にしているのを思い出しました。作業中はほとんど考えずに今やっていることに集中できるように...。
    もちろん、熟練者は複雑な物でもそれが出来てしまうのがすごいところですがw

    プログラミングはそれと少し違いますが、人間の頭ってのは、複雑なことを一度に考えるようにはできてないんでしょうね。
    そういう意味でも最初にいただいたアドバイスの「単純な要素に分解する」という言葉の重要性を感じます。

    返信削除