衝突判定

ここでは、立体の衝突判定について扱います。

- 目次 -

衝突判定とは

例えばゲームなど、 3DCGの舞台を動き回るようなプログラムの開発において、 重要な役割を担うのが、衝突判定の処理です。

衝突判定とは、その名前の通り、 立体同士の衝突を判定するための処理です。 例えば3DCGの舞台を動き回るようなプログラムでは、 主人公と地面、 または壁などとの衝突判定が必要不可欠となります。 主人公が地面と衝突している際はそれ以上落下しないように、 また壁と衝突した際は垂直に押し戻されるように、 衝突判定を使用して適切な処理を行う必要があります。

直線とポリゴンとの衝突判定

VCSSL Graphics3Dにおいて、衝突判定の基本となるのが、 直線とポリゴンとの衝突判定です。 これは、任意の原点と方向を持つ直線 (原点から一方向にのみ無限に伸びる、いわゆる半直線) と、ポリゴンとが交点を持つかどうかという判定です。

なお、直線とポリゴンとが交点を持つ場合には、 その交点の位置と、 交点におけるポリゴンの法線ベクトル (ポリゴンに垂直な方向を持ち、大きさが1のベクトル) の情報も得たい場合が多いでしょう。

直線とポリゴンとの交点位置や法線ベクトルは、 高校数学の教科書に載っているような公式で独自に求める事もできますが、 VCSSL Graphics3Dではあらかじめ用意されている getPolygonIntersection 関数で簡単に求める事が可能です。

- 関数仕様 -

bool getPolygonIntersection (
  int polygonID,
  int directionalVectorID, int pointVectorID,
  int intersectionVectorID, int normalVectorID
)

最初の引数polygonIDには判定対象のポリゴンのIDを、 続く引数 directionalVectorID と pointVectorID にそれぞれ直線の方向ベクトルと原点位置ベクトルのIDを指定します。 残る引数 intersectionVectorID と normalVectorID には、 交点の位置と法線ベクトル情報を格納するためのベクトルを用意して、 そのIDを指定します。なお、ポリゴンや直線のベクトルは、 あらかじめ共通の座標系へ座標変換を行ったものを指定する必要があります。

この関数はポリゴンと直線との位置関係を解析し、 交点が存在すればtrueを、存在しなければfalseを返します。 交点が存在する場合は、引数に指定された intersectionVectorID のベクトルに交点位置ベクトル情報が、 normalVectorID のベクトルに法線ベクトル情報が代入されます。

直線とモデルとの衝突判定

モデルはポリゴンの集合体なので、 直線とモデルとの衝突判定は、 直線とポリゴンとの衝突判定の繰り返しで行う事ができます。 つまりモデルを構成するすべてのポリゴンに対して、 直線との衝突判定を行います。 交点が複数存在する場合は、直線の原点に最も近い交点を検索します。 この処理はあらかじめ関数として用意されています。

モデルをポリゴンとの衝突判定を行うには、 getModelIntersection 関数を使用します。

- 関数仕様 -

bool getModelIntersection (
  int modelID,
  int directionalVectorID, int pointVectorID,
  int intersectionVectorID, int normalVectorID
)

最初の引数modelIDには判定対象のモデルのIDを、 続く引数 directionalVectorID と pointVectorID にそれぞれ直線の方向ベクトルと原点位置ベクトルのIDを指定します。 残る引数 intersectionVectorID と normalVectorID には、交点の位置と法線ベクトル情報を格納するためのベクトルを用意して、 そのIDを指定します。 なお、モデルや直線のベクトルは、 あらかじめ共通の座標系へ座標変換を行ったものを指定する必要があります。

この関数はモデルと直線との位置関係を解析し、 交点が存在すればtrueを、存在しなければfalseを返します。 交点が存在する場合は、 引数に指定されたintersectionVectorIDのベクトルに交点位置ベクトル情報が、 normalVectorIDのベクトルに法線ベクトル情報が代入されます。 交点が複数存在する場合は、直線の原点位置(pointVectorIDで指定) に最も近いものが選択されます。

ポリゴン同士の衝突判定

ポリゴン同士の衝突判定には、様々な方法が考えられます。 ここでは、直線とポリゴンとの衝突判定を応用した方法を解説します。

簡単のため、ポリゴンを正三角形のポリゴンとします。 まず、三角形の3つの頂点から、となりの頂点の方向へ半直線を3本引き、 V1、V2、V3と名づけます。右回りに引いても左回りに引いても構いません。

続いてV1、V2、V3と、別のポリゴンとの接触判定を行い、 交点の位置を求めます。 例えばV1と別のポリゴンとの間に交点が存在すれば、 V1の原点と交点との距離を求めます。

もしこの距離が、三角形の一辺の長さよりも小さければ、 この三角形と別のポリゴンは接触しています。 逆に大きい場合や、 そもそも交点が存在しない場合には、接触していません。

モデル同士の衝突判定

モデル同士の接触判定は、原理上、 ポリゴン同士の接触判定をあらゆるポリゴンの組に対して行えば、 厳密に行う事が可能です。 直方体などの少数ポリゴンモデル同士についてはそれでも良いでしょう。 しかし例えば1000ポリゴンのモデル同士の場合、 ポリゴンの組は1000×1000=100万通りもできてしまい、 素直に衝突判定を行っていたのでは処理が追いつきません。 そこでモデル同士の場合は、 ある程度処理を簡略化する必要が生じてくるでしょう。

モデル同士の距離で判定(バウンディングスフィア)

最も簡単な方法として挙げられるのが、 モデル同士の中心間距離で衝突判定を行う方法(バウンディングスフィア)です。 モデルの中心が原点になるように、 モデルをそれぞれ座標系に配置し、 あとは座標系の原点位置が一定の距離内に接近したら衝突と見なします。 この方法は非常に軽い負荷で処理する事が可能なので、 大量のモデル同士のおおまかな衝突判定に向いているでしょう。

適当な箇所から半直線(レイ)を伸ばし、ポリゴンとの交点で判定

もう少し詳細な衝突判定として、 半直線(レイ)との交点で衝突判定を行う事も考えられます。 まず、モデルの形状を考慮した上で、でっぱった箇所の先端など、 ある程度衝突しそうな箇所から、衝突しそうな方向へ半直線を延ばします。 そして、この半直線と別のモデルとの交点が、 一定以上近い距離に存在する場合に、モデル同士が衝突したと見なします。

描画用のモデルとは別に、衝突判定用の荒い形状を別に作る(バウンディングボックスなど)

描画用の細かい形状のモデルを囲むように、 おおまかな衝突判定用の形状を作るのも有用でしょう。 VCSSLでは、これは透明なポリゴンやモデルを作って、 描画用モデルに重ねて配置するなどで対応できます。 衝突用の形状は、 例えば直方体(バウンディングボックス)などが考えられます。

プログラム例

実際にワールド座標系上に箱型モデルを配置し、 上から点を落下させ、箱型表面ではね返してみましょう。 これには直線とモデルとの衝突判定を用います。 具体的には、落下する点から下向きに半直線を延ばし、 それとモデルとの交点が一定以内の距離に来ると、 落下速度の向きを反転させてはね返します。 以下のように記述し、実行してみてください。

Sample.vcssl

このプログラムを実行すると、 画面に白い箱とボールが表示されます。 ボールは落下していき、 ボールの表面で跳ね返ります。

実行結果、立方体上で跳ね返るボールの図
実行結果
開始と同時にボールが落下していき、箱の表面に衝突して跳ね返る。