射线盒交叉理论

Myx*_*Myx 20 algorithm 3d raytracing intersection

我想确定光线和盒子之间的交点.该框由其最小3D坐标和最大3D坐标定义,并且光线由其原点和它指向的方向定义.

目前,我正在为盒子的每个面形成一个平面,我正在将光线与平面相交.如果光线与平面相交,那么我检查交叉点是否实际位于框的表面上.如果是这样,我检查它是否是此光线的最近交点,并返回最近的交点.

我检查平面交叉点是否在盒子表面上的方式是通过一个函数

bool PointOnBoxFace(R3Point point, R3Point corner1, R3Point corner2)
{
  double min_x = min(corner1.X(), corner2.X());
  double max_x = max(corner1.X(), corner2.X());
  double min_y = min(corner1.Y(), corner2.Y());
  double max_y = max(corner1.Y(), corner2.Y());
  double min_z = min(corner1.Z(), corner2.Z());
  double max_z = max(corner1.Z(), corner2.Z());
  if(point.X() >= min_x && point.X() <= max_x && 
     point.Y() >= min_y && point.Y() <= max_y &&
     point.Z() >= min_z && point.Z() <= max_z)
     return true;

  return false;
}
Run Code Online (Sandbox Code Playgroud)

哪个corner1是该框面的矩形的一角,corner2是对面的角.我的实现大部分时间都在工作,但有时它给了我错误的交集.请看图片:

替代文字

图像显示来自相机眼睛并撞击盒子表面的光线.其他光线是盒子表面的法线.可以看出,特别是一条光线(它实际上是看到的法线)从盒子的"背面"出来,而法线应该从盒子的顶部出来.这似乎很奇怪,因为有多个其他光线正确地击中了盒子的顶部.

我想知道我正在检查交叉点是否在盒子上的方式是正确的还是我应该使用其他算法.

谢谢.

Rex*_*err 15

epsilon增加的东西实际上并不是一个很好的方法,因为你现在在你的盒子边缘有一个大小为epsilon的边框,光线可以穿过它.所以你将摆脱这个(相对常见的)奇怪的错误集,并最终得到另一组(更罕见的)奇怪的错误.

我假设你已经想象你的光线沿着它的矢量以某种速度行进并找到与每个平面相交的时间.因此,例如,如果您正在与平面相交x=x0,并且您的光线(rx,ry,rz)从方向开始(0,0,0),则交叉时间为t = x0/rx.如果t是否定的,请忽略它 - 你会走另一条路.如果t为零,你必须决定如何处理这种特殊情况 - 如果你已经在飞机上,你是否已经退回,或通过它?您可能还希望将其rx==0作为特殊情况处理(以便您可以触及方框的边缘).

无论如何,现在你已经准确地找到了击中那架飞机的坐标:它们是(t*rx , t*ry , t*rz).现在,你可以读出是否t*ryt*rz他们是需要在(即最小和最大沿这些轴的立方体之间)的矩形范围内. 你没有测试x坐标,因为你已经知道你 再次击中它,你必须决定是否/如何处理角落作为特殊情况.此外,现在您可以按时间对各种曲面进行碰撞,并选择第一个作为碰撞点.

这使您可以在不依赖于任意ε因子的情况下计算光线是否与您的立方体相交的位置,以及浮点运算的精确度.

所以,你只需要三个功能,如你已经得到了一个:一个测试你是否打内yz假设你打x,并为相应的人xz,并xy假设你打yz分别.


编辑:添加到(详细)的代码显示如何针对每个轴进行不同的测试:

#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....
}
Run Code Online (Sandbox Code Playgroud)

(我只是键入了这个,没有编译它,所以要小心bug.)(编辑:刚刚纠正了i- > first.)

无论如何,重点是你分别对待三个方向,并测试是否在(u,v)坐标中的右框内发生了撞击,其中(u,v)是(x,y),(x ,z)或(y,z)取决于您击中的平面.