ゲームプログラミング技術集 
ひっと

平面と線分の交点を求める方法

内積を使った平面と線分の交点を求める方法とプログラミング例。

平面と線分の交点を求める方法

平面と線分の交差判定

平面と線分ABから交点Xを求めるには...

はじめに、内積を使って平面と線分の交差判定を行います。
平面の平面方程式から平面上の点Pと法線Nが分かるので、
この状態において、PAベクトル、PBベクトルをそれぞれNと内積して、片方がプラス、片方がマイナスなら交差していると判断できます。
内積が0になることもありますが、それは線端が平面上にある場合です。
( PAとNの内積 >= 0 ) かつ ( PBとNの内積 <= 0 ) なら交差する
または
( PAとNの内積 <= 0 ) かつ ( PBとNの内積 >= 0 ) なら交差する

平面と線分の交点

前述の内積の結果は、AXとXBの距離の比率を表しているので、
ベクトルABをその比率で分割すれば交点Xが求まります。
AXの長さ : XBの長さ = PAとNの内積 : PBとNの内積
※内積はマイナス値になる場合があるので、絶対値を使ってください。
交点X = A + ベクトルAB * ( PAとNの内積 / (PAとNの内積 + PBとNの内積) )

平面と線分の交点 プログラミング例

#include <math.h>

//平面の定義
class Plane {	//ax+by+cz+d=0
public:
	double a,b,c,d;
	
	Plane(){}
	Plane(double a,double b,double c,double d){ this->a = a; this->b = b; this->c = c; this->d = d; }
};

//ベクトルの定義
class Vector3D{
public:
	double x,y,z;
	
	Vector3D(){}
	Vector3D( double x, double y, double z) {this->x = x; this->y = y; this->z = z; }
};

//頂点の定義(ベクトルと同じ)
#define Vertex3D Vector3D


//線分ABと平面の交点を計算する
bool IntersectPlaneAndLine(
	Vertex3D* out, //戻り値 交点が見つかれば格納される
	Vertex3D  A,   //線分始点
	Vertex3D  B,   //線分終点
	Plane     PL ) //平面
{	
	//平面上の点P
	Vertex3D P = Vertex3D( PL.a * PL.d, PL.b * PL.d, PL.c * PL.d );

	//PA PBベクトル
	Vector3D PA = Vertex3D( P.x - A.x, P.y - A.y, P.z - A.z );
	Vector3D PB = Vertex3D( P.x - B.x, P.y - B.y, P.z - B.z );

	//PA PBそれぞれを平面法線と内積
	double dot_PA = PA.x * PL.a + PA.y * PL.b + PA.z * PL.c;
	double dot_PB = PB.x * PL.a + PB.y * PL.b + PB.z * PL.c;

	//これは線端が平面上にあった時の計算の誤差を吸収しています。調整して使ってください。
	if ( abs(dot_PA) < 0.000001 ) { dot_PA = 0.0; }	
	if ( abs(dot_PB) < 0.000001 ) { dot_PB = 0.0; }

	//交差判定
	if( dot_PA == 0.0 && dot_PB == 0.0 ) {
		//両端が平面上にあり、交点を計算できない。
		return false;
	} else
	if ( ( dot_PA >= 0.0 && dot_PB <= 0.0 ) ||
	     ( dot_PA <= 0.0 && dot_PB >= 0.0 ) ) {
		 //内積の片方がプラスで片方がマイナスなので、交差している
		 
	} else {
		//交差していない
		return false;
	}

	//以下、交点を求める 

	Vector3D AB = Vector3D( B.x - A.x, B.y - A.y, B.z - A.z );

	//交点とAの距離 : 交点とBの距離 = dot_PA : dot_PB
	double hiritu = abs(dot_PA) / ( abs(dot_PA) + abs(dot_PB) );

	out->x = A.x + ( AB.x * hiritu );
	out->y = A.y + ( AB.y * hiritu );
	out->z = A.z + ( AB.z * hiritu );

	return true;
}

戻る