通过 epsilon 增加事物实际上并不是一个很好的方法,因为您现在在盒子的边缘有一个大小为 epsilon 的边框,光线可以通过它。所以你会摆脱这组(相对常见的)奇怪的错误,并最终得到另一组(罕见的)奇怪的错误。
我假设您已经在设想您的光线沿着其矢量以某种速度行进,并找到与每个平面相交的时间。因此,例如,如果您在 处与平面相交,并且您的光线从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 坐标,因为您已经知道您击中了它 再次,您必须决定是否/如何处理击中角落作为特殊情况。此外,现在您可以按时间排序与各种表面的碰撞,并选择第一个作为碰撞点。
这允许您在不使用任意 epsilon 因子的情况下计算您的光线是否以及在何处与您的立方体相交,以达到浮点运算可能的精度。
因此,您只需要三个函数,就像您已经拥有的函数一样:一个用于测试您是否在yz
假设您 hit的情况下击中x
,以及相应的xz
假设xy
您击中y
and的函数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
。)
无论如何,关键是你分别对待三个方向,并测试是否在(u,v)坐标的右侧框内发生了影响,其中(u,v)是(x,y),(x ,z) 或 (y,z) 取决于您击中的飞机。