レイトレース:3角形と視線の交差判定


~ Ray Tracing : Intersection with triangle ~







■はじめに

球の交差判定ができるようになったら、次は3角形との交差判定です。
3角形の交差判定ができれば、ポリゴンモデルでの交差判定ができるようになるので、世界が一気に広がります。

今回のプログラムは、次のものです。

まぁ、いつものように適当にファイルが入っています。
APP WIZARD から出力されるフレームワークのファイルは紹介を省かせていただきます。

render.cppレイトレ用の描画関数群
render.hレイトレ用の描画関数群
mainDlg.cppダイアログを管理するクラスのメソッドが書かれたファイル
mainDlg.hダイアログを管理するクラスのヘッダ

あと、実行ファイル、リソースファイル、プロジェクトファイルが入っています。

■何やってるの?

3角形とレイの交差判定ですが、2段階の手順を踏みます。
第1段階では、レイと3角形の作る平面との交点を求めます。
第2段階では、交点が3角形の中に入っているかどうかを調べます。

レイと3角形の作る平面との交点pの求め方ですが、 レイの方程式

p = x + t v

と、3角形の作る平面の方程式

(p - p0)・n = 0

を連立して、求めます。ここで、xはレイの出る点、vはレイの方向、 p0は3角形の1点、nは3角形の法線ベクトルです。

連立した結果、レイの進んだ距離tは、

    (x - p0)・n
t = ------------
        vn

になります。
ここで、vn=0 の時は、レイと3角形の面に平行に進んでいるので、交わりません。
また、t<0の時は、視線の後ろ側に交点があるので、やはり交わることがありません。

次に、この交点が3角形の内部にあるかどうかの判定をします。
この判定には、外積を使います。
交点と3角形を結ぶベクトルd0と、3角形の1辺d1との外積を計算します。
すると、外積によってできたベクトルは、3角形の法線と同じ方向か反対の方向を向きます。
この向きは、交点が3角形の中にあるか外にあるかで符号が変わるので、全ての辺に関して、符号が同じならば、交点は3角形の中にあることになります。

実際のプログラムでは、次のようになります。
このプログラムは、レイが3角形に衝突したら、黄色(RGB=(1, 0.9, 0.5))を返し、外れたらBG_COLORを返す関数です。
とりあえず、3角形の頂点は決めうちにして、コードの中に埋め込みました。

render.cpp
0115: D3DXVECTOR3 *GetColor(D3DXVECTOR3 *dest, D3DXVECTOR4 *x, D3DXVECTOR4 *v)
0116: {
0117:     D3DXVECTOR4 d0, d1;
0118:     D3DXVECTOR3 c;
0119:     D3DXVECTOR4 normal;
0120: 
0121:     // 3角形の各頂点
0122:     D3DXVECTOR4 pos[3] = {
0123:         D3DXVECTOR4( 0.000f, 0.866f, 0.0f, 1),
0124:         D3DXVECTOR4( 0.866f,-0.500f, 0.0f, 1),
0125:         D3DXVECTOR4(-0.866f,-0.500f, 0.0f, 1),
0126:     };
0127: 
0128:     *dest = BG_COLOR;   // 外れたときの色
0129: 

最初に頂点から法線ベクトルを計算します。
法線ベクトルは、2つの辺の外積を正規化して求めます。
データで持ってもいいでしょう。

render.cpp
0130:     // 法線ベクトルの計算
0131:     D3DXVECTOR4 t0 = pos[1]-pos[0];
0132:     D3DXVECTOR4 t1 = pos[2]-pos[0];
0133:     D3DXVec3Cross((D3DXVECTOR3*)&normal, (D3DXVECTOR3*)&t1, (D3DXVECTOR3*)&t0);
0134:     D3DXVec3Normalize((D3DXVECTOR3*)&normal, (D3DXVECTOR3*)&normal);
0135: 
0136: 

次に、レイがすすだ量のパラメータtを計算します。
今回は、裏を向いているポリゴンとの交差判定はしないようにします。
それは、レイと法線ベクトルの向きが反対の場合(vn<0)は、外れた事にするという処理で行います。
また、3角形がレイと平行なときの条件は、少しゆとりを持って、(0<vn & vn<0.00001f)で判定します。
この2つは、1つの式で一度に判定します。
tが求まった後で、交点がレイの後ろにあるかどうかの判定をします。

render.cpp
0137:     // 線分との判定
0138:     D3DXVECTOR4 xp = pos[0]-(*x);
0139:     FLOAT xpn = D3DXVec3Dot((D3DXVECTOR3 *)&xp, (D3DXVECTOR3 *)&normal);
0140:     FLOAT vn  = D3DXVec3Dot((D3DXVECTOR3 *)v,   (D3DXVECTOR3 *)&normal);
0141:     
0142:     if(-0.00001f<=vn) return dest;// カリングと発散を外す
0143: 
0144:     float t = xpn/vn;
0145:     
0146:     if(t<0) return dest;// 後ろ向きのレイは無効
0147: 

次に交点を求めて、交点が3角形の内部にあるか判定をします。

render.cpp
0148:     D3DXVECTOR4 p = (*x)+t*(*v);// 3角形の平面への射影
0149: 
0150:     // 射影した点が3角形の内側にあるかテスト
0151:     d0 = p-pos[0];
0152:     d1 = pos[1]-pos[0];
0153:     D3DXVec3Cross(&c, (D3DXVECTOR3*)&d0, (D3DXVECTOR3*)&d1);
0154:     if(D3DXVec3Dot(&c, (D3DXVECTOR3 *)&normal)<0) return dest;
0155:     d0 = p-pos[1];
0156:     d1 = pos[2]-pos[1];
0157:     D3DXVec3Cross(&c, (D3DXVECTOR3*)&d0, (D3DXVECTOR3*)&d1);
0158:     if(D3DXVec3Dot(&c, (D3DXVECTOR3 *)&normal)<0) return dest;
0159:     d0 = p-pos[2];
0160:     d1 = pos[0]-pos[2];
0161:     D3DXVec3Cross(&c, (D3DXVECTOR3*)&d0, (D3DXVECTOR3*)&d1);
0162:     if(D3DXVec3Dot(&c, (D3DXVECTOR3 *)&normal)<0) return dest;
0163:     

最後に(ここまで来たら、レイは3角形とあたっているので)、あたったときの色を出力します。

render.cpp
0164:     // 当たったときの色
0165:     *dest = D3DXVECTOR3( 1.0f, 0.9f, 0.5f );
0166:     
0167:     return dest;
0168: }

■最後に

さぁ、ポリゴンとの判定ができるようになりました。
実際にメッシュとの判定をするときには、高速化を考えなくてはなりませんが、 最初はこんなもんでしょう。





もどる

imagire@gmail.com