イプシロンで物事を増やすことは、実際にはこれを行うための優れた方法ではありません。これは、光線が通過できるボックスの端にイプシロンのサイズの境界線があるためです。したがって、この (比較的一般的な) 奇妙なエラー セットを取り除くと、別の (よりまれな) 奇妙なエラー セットが発生します。
光線がそのベクトルに沿ってある速度で移動し、各平面と交差する時間を見つけていることをすでに想像していると思います。したがって、たとえば、平面と で交差していてx=x0
、光線が(rx,ry,rz)
からの方向に進んでいる(0,0,0)
場合、交差の時間は ですt = x0/rx
。が負の場合t
は無視してください。逆の方向に進んでいます。がゼロの場合t
、その特殊なケースをどのように処理するかを決定する必要があります。すでに飛行機に乗っている場合、飛行機で跳ね返りますか、それとも通り抜けますか? また、特別なケースとして処理することもrx==0
できます (ボックスの端にヒットできるようにするため)。
とにかく、これで飛行機に衝突した正確な座標がわかりました(t*rx , t*ry , t*rz)
。t*ry
これで、 と が必要な長方形内にあるかどうかを読み取ることができますt*rz
(つまり、これらの軸に沿った立方体の最小値と最大値の間)。 x 座標はテストしません。これは、ヒットしたことを既に知っているためです。 ここでも、特殊なケースとしてコーナーへのヒットを処理するかどうか/どのように処理するかを決定する必要があります。さらに、さまざまなサーフェスとの衝突を時間で並べ替えて、最初のサーフェスを衝突ポイントとして選択できるようになりました。
これにより、任意のイプシロン係数に頼ることなく、光線がキューブと交差するかどうか、およびどこで交差するかを、浮動小数点演算で可能な精度で計算できます。
したがって、すでに持っているような 3 つの関数yz
が必要x
です。xz
xy
y
z
編集:(詳細に)追加されたコードは、軸ごとに異なるテストを行う方法を示しています:
#define X_FACE 0
#define Y_FACE 1
#define Z_FACE 2
#define MAX_FACE 4
// true if we hit a box face, false otherwise
bool hit_face(double uhit,double vhit,
double umin,double umax,double vmin,double vmax)
{
return (umin <= uhit && uhit <= umax && vmin <= vhit && vhit <= vmax);
}
// 0.0 if we missed, the time of impact otherwise
double hit_box(double rx,double ry, double rz,
double min_x,double min_y,double min_z,
double max_x,double max_y,double max_z)
{
double times[6];
bool hits[6];
int faces[6];
double t;
if (rx==0) { times[0] = times[1] = 0.0; }
else {
t = min_x/rx;
times[0] = t; faces[0] = X_FACE;
hits[0] = hit_box(t*ry , t*rz , min_y , max_y , min_z , max_z);
t = max_x/rx;
times[1] = t; faces[1] = X_FACE + MAX_FACE;
hits[1] = hit_box(t*ry , t*rz , min_y , max_y , min_z , max_z);
}
if (ry==0) { times[2] = times[3] = 0.0; }
else {
t = min_y/ry;
times[2] = t; faces[2] = Y_FACE;
hits[2] = hit_box(t*rx , t*rz , min_x , max_x , min_z , max_z);
t = max_y/ry;
times[3] = t; faces[3] = Y_FACE + MAX_FACE;
hits[3] = hit_box(t*rx , t*rz , min_x , max_x , min_z , max_z);
}
if (rz==0) { times[4] = times[5] = 0.0; }
else {
t = min_z/rz;
times[4] = t; faces[4] = Z_FACE;
hits[4] = hit_box(t*rx , t*ry , min_x , max_x , min_y , max_y);
t = max_z/rz;
times[5] = t; faces[5] = Z_FACE + MAX_FACE;
hits[5] = hit_box(t*rx , t*ry , min_x , max_x , min_y , max_y);
}
int first = 6;
t = 0.0;
for (int i=0 ; i<6 ; i++) {
if (times[i] > 0.0 && (times[i]<t || t==0.0)) {
first = i;
t = times[i];
}
}
if (first>5) return 0.0; // Found nothing
else return times[first]; // Probably want hits[first] and faces[first] also....
}
(私はこれを入力しただけで、コンパイルしていないので、バグに注意してください。)(編集:i
->を修正しただけfirst
です。)
とにかく、ポイントは、3 つの方向を別々に扱い、衝撃が (u,v) 座標の右側のボックス内で発生したかどうかをテストすることです。ここで、(u,v) は (x,y)、(x) のいずれかです。 ,z) または (y,z) は、ヒットする平面に応じて異なります。