Вычисление точки пересечения луча с Треугольником для построения тени

 

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

       Уравнение плоскости выглядит следующим образом:

 

                              Q(x, y, z) = Ax + By + Cz +D = 0.                                  (2.2.5.3)

 

       Здесь коэффициенты A, B, C совпадают с координатами нормали к этой плоскости. Координаты нормали плоскости совпадают с координатами нормали треугольника, которые мы посчитали на этапе загрузки сцены.

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

 

                                          D = –Ax –By – Cz.                                              (2.2.5.4)

 

       По ходу выполнения программы значение D меняться не будет, поэтому его целесообразно посчитать при инициализации сцены и хранить, как и координаты нормали. Пересчитывать его необходимо только при изменении положения треугольника.

       Теперь для нахождения точки пересечения подставим уравнения луча в

уравнение плоскости.

 

                   A (x1 + at) + B (y1 + bt) + C (z1 + ct) + D = 0

 

Откуда получим

 

              t = – (Ax1 + By1 + Cz1 + D) / (Aa + Bb + Cc)                            (2.2.5.5)

 

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

       Для нахождения координат точки пересечения надо подставить найденное значение параметра t в уравнения луча (2). Назовем точку пересечения D. Мы получим координаты xD, yD, zD.

       Теперь необходимо определить, попала ли точка D внутрь треугольника. Найдем координаты векторов AB, BC, CA (A, B, C – вершины треугольника) и координаты векторов AD, BD, CD. Затем найдем три векторных произведения:

 

nA = AB x AD,

nB = BC x BD,                                                    (2.2.5.6)

nC = CA x CD.

 

       Эти вектора будут коллинеарные. Если все три вектора сонаправлены, то точка D лежит внутри треугольника. Сонаправленность определяется равенству знаков соответствующих координат всех трех векторов.

       Операцию проверки принадлежности точки D треугольнику ABC можно ускорить. Если ортогонально спроецировать треугольник ABC и точку D на одну из плоскостей xOy, yOz или xOz, то попадание проекции точки в проекцию треугольника будет означить попадание самой точки в треугольник (конечно же, если уже известно, что точка D лежит в плоскости, содержащей треугольник ABC). При этом число операций заметно сокращается. Так для поиска координат всех векторов нужно искать по две координаты на каждый вектор, а при поиске векторных произведений нужно искать только одну координату (остальные равны нулю).

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

Кроме того, перед вычислениями векторов и векторных произведений можно провести простой габаритный тест. Если проекция точки D лежит правее, левее, выше или ниже каждой из проекций вершин треугольника, то она не может лежать внутри.

Остается добавить, что для проецирования лучше выбирать ту из плоскостей, площадь проекции треугольника на которую больше. При таком условии исключается случай проецирования треугольника в отрезок (при условии, что проверяемый треугольник не вырожден в отрезок). Кроме того, при увеличении площади проекции уменьшается вероятность ошибки. Для определения такой плоскости проецирования достаточно проверить три координаты нормали треугольника. Если z-координата нормали больше (по абсолютному значению) x и y, то проецировать надо на плоскость xOy. Если y больше чем x и z, то проецируем на xOz. В оставшемся случае – на yOz.

 

 

bool RayTriangleIntersect(Vector RayPos, Vector RayDir, Vector P1, Vector P2, Vector P3,

                              out Vector ResultPoint, out Vector ResultNormal)

   {

       const double EPSILON2 = 0.00001;

   Vector v1 = P2 - P1;

   Vector v2 = P3 - P1;

   Vector pvec = RayDir ^ v2;

   double det = v1 * pvec;

   ResultPoint = new Vector();

       ResultNormal = new Vector();

       if ((det < EPSILON2) && (det > -EPSILON2))

   {

          return false;

   }

       double invDet = 1 / det;

   Vector tvec = RayPos - P1;

       double u = (tvec * pvec) * invDet;

   if ((u < 0) || (u > 1))

          return false;

   else

   {

          Vector qvec = tvec ^ v1;

           double v = (RayDir * qvec) * invDet;

          if ((v < 0) || (u+v > 1))

                return false;

           double t = (v2 * qvec) * invDet;

          if (t > 0)

          {

                ResultPoint = RayPos + RayDir * t;

                ResultNormal = v1 ^ v2;

               return true;

          }

          else

                return false;

   }

   }

 

 


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: