Перекрестная теория поля луча

Я хочу определить точку пересечения между лучом и полем. Поле определяется к его минуте, 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;
}

где corner1 один угол прямоугольника для того торца конца штанги с внутренней резьбой и corner2 противоположный угол. Моя реализация работает большую часть времени, но иногда она дает мне неправильное пересечение. См. изображение:

alt text

Изображение показывает лучи, прибывающие из глаза камеры и поражающие поверхность поля. Другие лучи являются normals на поверхность поля. Таким образом один луч в особенности (это - на самом деле нормальное, которое замечено) прибывает из "задней части" поля, тогда как нормальное должно подходить от вершины поля. Это, кажется, странно, так как существует несколько других лучей, которые поражают вершину поля правильно.

Я задавался вопросом, является ли способ, которым я проверяю, ли точка пересечения на поле, корректен или если я должен использовать некоторый другой алгоритм.

Спасибо.

20
задан Myx 1 April 2010 в 21:42
поделиться

5 ответов

Увеличение значений с помощью 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 внутри прямоугольника, в котором они должны находиться (то есть между min и max для куба по этим осям). Вы не проверяете координату x, потому что уже знаете, что попали в нее. Опять же, вы должны решить, следует ли / как обрабатывать удары по углам как особый случай. Кроме того, теперь вы можете упорядочить столкновения с различными поверхностями по времени и выбрать первую в качестве точки столкновения.

Это позволяет вычислить, не прибегая к произвольным эпсилон-факторам, пересекает ли и где ваш луч ваш куб, с точностью, возможной с помощью арифметики с плавающей запятой.

Итак, вам просто нужны три функции, подобные той, что у вас уже есть: одна для проверки того, попали ли вы в 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....
}

(Я просто набрал это, не скомпилировал,так что остерегайтесь ошибок.) (Edit: только что исправил i -> first .)

В любом случае, дело в том, что вы обрабатываете три направления отдельно , и проверьте, произошло ли столкновение в правом поле в координатах (u, v), где (u, v) равны (x, y), (x, z) или (y, z) в зависимости от в какой самолет вы попали.

15
ответ дан 30 November 2019 в 01:18
поделиться

PointOnBoxFace должен быть двухмерным, а не трехмерным. Например, если вы тестируете плоскость z = z_min , вам нужно только сравнить x и y с соответствующими границами. Вы уже выяснили, что координата z верна. Точность с плавающей запятой, вероятно, сбивает вас с толку, когда вы «перепроверяете» третью координату.

Например, если z_min равно 1.0, вы сначала проверяете эту плоскость. Вы найдете точку пересечения ( x , y , 0,999999999). Теперь, хотя x и y находятся в определенных пределах, z не совсем правильное.

2
ответ дан 30 November 2019 в 01:18
поделиться

Может быть, этот луч проходит точно через край коробки? Ошибки округления с плавающей запятой могут привести к тому, что оно будет пропущено как правой, так и задней гранью.

0
ответ дан 30 November 2019 в 01:18
поделиться

РЕДАКТИРОВАТЬ: Игнорируйте этот ответ (см. Комментарии ниже, где я довольно убедительно показал ошибку моего пути).

Вы проверяете, находится ли точка внутри объема, но точка находится на периферии этого объема, поэтому вы можете обнаружить, что это «бесконечно малое» расстояние за пределами объема. Попробуйте увеличить прямоугольник на небольшой эпсилон, например:

double epsilon = 1e-10; // Depends the scale of things in your code.
double min_x = min(corner1.X(), corner2.X()) - epsilon;
double max_x = max(corner1.X(), corner2.X()) + epsilon;
double min_y = min(corner1.Y(), corner2.Y()) - epsilon;
...

Технически правильный способ сравнения почти равных чисел - это преобразовать их битовые представления в целые числа и сравнить целые числа для некоторого небольшого смещения, например, в C:

#define EPSILON 10 /* some small int; tune to suit */
int almostequal(double a, double b) {
    return llabs(*(long long*)&a - *(long long*)&b) < EPSILON;
}

Конечно, в C # это не так просто, но, возможно, небезопасная семантика может дать такой же эффект. (Спасибо @Novox за его комментарии, которые привели меня к красивой странице , где подробно объясняется этот метод.)

0
ответ дан 30 November 2019 в 01:18
поделиться

Код выглядит нормально. Попробуйте найти именно этот луч и отладить его.

0
ответ дан 30 November 2019 в 01:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: