球の交差判定ができるようになったら、次は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 = ------------ v・n
になります。
ここで、v・n=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: }
さぁ、ポリゴンとの判定ができるようになりました。
実際にメッシュとの判定をするときには、高速化を考えなくてはなりませんが、
最初はこんなもんでしょう。