Skip to content

enchant.jsで3Dポリゴンの表示に挑戦! #1

やあshi3zだ。

「サウンド対応」がテーマとなった今回の9leap 9Days Challengeにもかなりの力作が投稿されてきた。審査期間一杯までバージョンアップが認められているので、まだ未完成のものも少なくないが、今後に多いに期待できる作品が勢揃いしている。

さて、昨日が〆切だったので、今日は一休みしているプログラマーたちも多いと思う。
僕も教育ITソリューションEXPOで疲弊し切ったので、気分転換にプログラムを書いてみたぞ!

今回のテーマは、なんとポリゴンだ。
まずは下のサンプルを実行してみてほしい。

enchant.jsでポリゴンを表示するサンプル #1 – jsdo.it – share JavaScript, HTML5 and CSS

ソースコードは以下のような感じ。

enchant.jsでポリゴンを表示するサンプル #1 – jsdo.it – share JavaScript, HTML5 and CSS

ではいつものように要点だけ解説するぞ!

今回、手を加えたのはもっぱらShapeクラスだ。
まずinitializeから見てみよう。

    initialize:function(){
        this.vt =new VertexTable();
        var vt =this.vt;
        vt.push(new Vector( -20, -20, -20) ); //0
        vt.push(new Vector( -20, -20,  20) ); //1
        vt.push(new Vector( -20,  20, -20) ); //2
        vt.push(new Vector( -20,  20,  20) ); //3
        vt.push(new Vector(  20, -20, -20) ); //4
        vt.push(new Vector(  20, -20,  20) ); //5
        vt.push(new Vector(  20,  20, -20) ); //6
        vt.push(new Vector(  20,  20,  20) ); //7

まず頂点の定義。
前回までのワイヤーフレームのサンプルでは、実はここをかなり手抜きしていた。
本来、立方体の頂点は8つしかない。

今回定義しているのは8つだけだ。
それぞれ、0から7までの番号を振ってある。これが頂点配列であるvtの添字として使えるわけだね。

わかりやすいように図にしてみた。

頂点データとは別に、立方体には面(ポリゴン)が6面ある。
これを定義しているのが続く部分だ。

        this.polygon = [];
        var polygon=this.polygon;
        polygon.push([0,2,3,1]);
        polygon.push([4,6,2,0]);
        polygon.push([2,6,7,3]);
        polygon.push([7,6,4,5]);
        polygon.push([5,4,0,1]);
        polygon.push([7,5,1,3]);

これで6つのポリゴンを定義できた。
次に改造が加わっているのは、drawメソッドだ。

        var cols=["#f00","#00a","#0a0","#0aa","#aa0","#a0a"];
        var pl = this.polygon;
        for(var i=0;i<pl.length;i++){
            var polygon = pl[i];
            context.beginPath();
            if(i==0)
                context.strokeStyle="#fff";
            else
                context.strokeStyle=this.color;
               
            context.fillStyle = cols[i];
           
            //面の裏表を見極める
            var ax = rvt[polygon[1]].x/rvt[polygon[1]].w-rvt[polygon[0]].x/rvt[polygon[0]].w;
            var ay = rvt[polygon[1]].y/rvt[polygon[1]].w-rvt[polygon[0]].y/rvt[polygon[0]].w;
            var bx = -rvt[polygon[polygon.length-1]].x/rvt[polygon[polygon.length-1]].w+
                                            rvt[polygon[0]].x/rvt[polygon[0]].w;
            var by = -rvt[polygon[polygon.length-1]].y/rvt[polygon[polygon.length-1]].w+
                                            rvt[polygon[0]].y/rvt[polygon[0]].w;

            if( (ax*by-ay*bx) >0){
                context.moveTo(rvt[polygon[0]].x/rvt[polygon[0]].w,rvt[polygon[0]].y/rvt[polygon[0]].w);
                for(var j=1;j<polygon.length;j++){
                    context.lineTo(rvt[polygon[j]].x/rvt[polygon[j]].w,rvt[polygon[j]].y/rvt[polygon[j]].w);
                }
                context.fill();
                context.closePath();
            }
        }

前回のサンプルでは、ただ頂点配列vtを順に線で描画して行っただけだが、今回はポリゴン単位で描画している。

基本的にはmoveToとlineToを呼んで、fillで塗りつぶすことが出来る。

さて、ポリゴンを描画する時はワイヤーフレームと違って、ポリゴンの表と裏について判別する必要がある。

そのときに使うのは、ポリゴンの定義された頂点の順序だ。

例えば、一枚目のポリゴンは、上図のような順序で定義してある。
試しにこの部分の順序を逆にしてみると、表示が乱れるのがわかると思う。

この表から見て時計回り(専門用語でCW;Clockwiseとも呼ぶ)に定義されたポリゴンを実際に画面上に投影したとき、反時計回りになっていたら、それは裏側を向いているということになる。

これを判定するためには二次元ベクトルの外積を求める。

外積は、二つのベクトルが構成する平面に対して垂直になるようなベクトルとして結果が返ってくる。

これを上手く使うと、時計回りか反時計回りか判定することができる。外積ベクトルのz値だけに注目し、それが0以上の時は表、と判定するのだ。
このテクニックは3Dプログラミングに限らずよく利用する。

ふたつのベクトルaとbの外積のz値は次のような式で求めることが出来る

ax*by-ay*bx

drawメソッドの中では、ポリゴンの最初の頂点を基準にして、最初の頂点polygon[0]から次の頂点polygon[1]へのベクトルをa、最後の頂点polygon[polygon.length-1]から最初の頂点polygon[0]へのベクトルをbとして、外積を求めている。

ただし、そのままでは同次座標になっているので、それぞれの要素をwで割ってから外積の計算を行っている。

これにより、裏側を向いていて本来描画すべきでないポリゴンは描画しないようにする。

以上の処理を入れると、「一応は」ポリゴンを描画出来る。

ポリゴンはワイヤーフレームと違って見えにくいので立方体の大きさを元々の二倍にした。

しかし本格的なゲームを作るには、これだけではまだ不十分なのだ。
どの部分が不十分なのか。

それを解決するにはどうすればいいのか?

サンプルを実際に動かしてみてよーく見てみよう。
それではまた次回

このエントリーをはてなブックマークに追加
はてなブックマーク - enchant.jsで3Dポリゴンの表示に挑戦! #1
Post to Google Buzz
Share on GREE

Related posts:

  1. enchant.jsで3Dゲームを作ってみる!
  2. Canvasで3Dワイヤーフレームに挑戦!

Facebook comments:

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*