Как найти точку пересечения луча и плоскости треугольника?
Хочу разобраться с 3D-моделированием, соответственно, реализация на готовых движках на данный момент совершенно не интересует.
Действую по стандартной стратегии: выпустить луч от камеры в направлении пикселя на экране, затем найти точку пересечения луча и плоскости, в которой лежит полигон (треугольник). Для нахождения уравнения плоскости полигона хочу использовать метод Гаусса, луч задаю пересечением двух плоскостей (2 коэффициента назначаю сам, остальные 2 ищу тем же методом Гаусса - нахожу уравнение плоскости, в которой лежит луч, повторяю операцию с другими начальными коэффициентами - получаю уравнение другой плоскости). Далее просто подставляю в матрицу 3х3 коэффициенты трех плоскостей и нахожу точку их пересечения.
Проблема заключается в поиске уравнения плоскости полигона в случае, когда эта плоскость проходит через точку (0, 0, 0), так как я изначально выбираю коэффициент D = 1 (для такой плоскости он всегда будет равен 0). Не знаете ли вы, как обычно решают эту проблему?
UPDATE: Вот решение, которое получилось у меня:
Вместо поиска уравнений плоскостей, действительно проще работать сразу с векторами. Пусть плоскость задается тремя точками: A, B, C. А прямая двумя: D, E. Точка T - пересечение прямой с плоскостью. Можно воспользоваться двумя свойствами векторов:
- Смешанное произведение компланарных векторов = 0
- Векторное произведение коллинеарных векторов = нулевому вектору
Вот так получается матрица координат точки T:

Дальше можно решать тем же методом Гаусса. При таком подходе матрица не будет иметь решения методом Гаусса только в двух случаях: когда прямая лежит в плоскости и когда они параллельны
Ответы (2 шт):
Пользуюсь вот таким методом (емнип основан на https://gamedev.stackexchange.com/a/25082/3644) для нахождения точки пересечения луча и треугольника (без плоскостей):
function RayTriangleIntersect(const aRay: TKMVertex3Ray; const V1, V2, V3: TKMVertex3; out aPoint: TKMVertex3): Boolean;
var
rayDir, E1, E2, PV, QV, TV: TKMVertex3;
T, U, V, Det, InvDet: Single;
begin
Result := False;
rayDir := aRay.Endpoint - aRay.Start;
E1 := V2 - V1;
E2 := V3 - V1;
PV := VectorCrossProduct(rayDir, E2);
Det := VectorDotProduct(E1, PV);
// Skip if Ray is parallel to triangle
if Abs(Det) < 0.00001 then
Exit;
InvDet := 1 / Det;
TV := aRay.Start - V1;
U := VectorDotProduct(TV, PV) * InvDet;
if (U >= 0) and (U <= 1) then
begin
QV := VectorCrossProduct(TV, E1);
V := VectorDotProduct(rayDir, QV) * InvDet;
if (V >= 0) and (U + V <= 1) then
begin
T := VectorDotProduct(E2, QV) * InvDet;
if T > 0.00001 then
begin
aPoint := TKMVertex3.NewLerp(aRay.Start, aRay.Endpoint, T);
Result := True;
end;
end;
end;
end;
Если надо просто вернуть да/нет пересечения луча с треугольником, то это одно, а если и координаты точки пересечения с треугольником или объектом с меш сеткой, то придётся усложнить код. В GLScene для этого есть вся необходимая иерархия 3д объектов и методов. Вот пример из ..\GLScene\Examples\Demos\collisions\RayBox\fRayBoxD.pas нахождения пересечения луча с кубом -
RayDir := AffineVectorMake(Random * 2 - 1, Random * 2 - 1, Random * 2 - 1);
NormalizeVector(RayDir);
GLLines1.Nodes.Clear;
GLLines1.Nodes.AddNode(RayStart);
GLLines1.Nodes.AddNode(VectorAdd(RayStart, VectorScale(RayDir, 8)));
GLPoints1.Positions.Clear;
GLPoints1.Positions.Add(RayStart);
GLPoints1.Positions.Add(BoxPos);
GLPoints1.Positions.Add(VectorSubtract(BoxPos, afScale));
GLPoints1.Positions.Add(VectorAdd(BoxPos, afScale));
if RayCastBoxIntersect(RayStart, RayDir, VectorSubtract(BoxPos, afScale),
VectorAdd(BoxPos, afScale), @iPnt) then
begin
Label1.Caption := Format('Intersect point: %.3f %.3f %.3f', [iPnt.X, iPnt.Y, iPnt.Z]);
GLPoints1.Positions.Add(iPnt);
beep;
end
else
Label1.Caption := 'no intersection';
Т.е. нашли точку пересечения, для сложных объектов в иерархии, состоящих из многих треугольников типа TGLMesh, TGLFreeForm, TGLActor есть и встроенные в классы методы нахождения пересечений помимо коллизии с другими объектами.