找到两条光线的交叉位置

时间:2015-03-21 22:15:45

标签: c# vector 3d trigonometry raycasting

我有两条光线。每条射线都有一个起始位置矢量(Vector3D)和一个方向矢量(Vector3D),但是继续到无穷远。它们都在同一平面上,但在3D环境中。光线是相互依赖的,这意味着它们可能不会完美地相互映射。由此我需要计算这些光线在3D环境中相交的位置并将其作为矢量输出。实质上:测距仪。

我该怎么做呢?有没有比使用C#Ray结构更好的方法呢,它甚至可能吗?

我是一个非常新的程序员(阅读:不好),但任何答案都表示赞赏,如果包含解释,我会很高兴。

光线的粗糙图像

enter image description here

2 个答案:

答案 0 :(得分:2)

3D空间中的两条线只有在同一平面上才会相交。空间中两条随机线相交的概率非常小。

当你想知道两条光线是否相交时,如果你正在寻找一个精确的交叉点,那么由于浮点误差你可能无法计算它。

下一个最好的事情是f ind两条光线之间的最短距离。然后,如果该距离小于某个阈值(由您定义),我们可以说光线相交。


寻找最短距离

这是3D空间中的两条光线,蓝色矢量代表最短距离。

Shortest distance between two rays

让我们从那个gif中选择一个框架:

Shortest distance 2

图例:

  • p1ray1.Position
  • p2ray2.Position
  • d1ray1.Direction
  • d2ray2.Direction
  • d3是叉积d1 x d2

光线方向的交叉积将垂直于两条光线,因此它是从光线到光线的最短方向。如果线是平行的,则叉积将为零,但是现在只允许处理非平行线。

从照片中,我们得到了等式:

  • p1 + a*d1 + c*d3 = p2 + b*d2

重新排列,以便变量位于左侧:

  • a*d1 - b*d2 + c*d3 = p2 - p1

由于每个已知值(d1,d2,d3,p1和p2)都有三个分量(x,y,z),因此这是一个由3个变量组成的三个线性方程组。

  • a*d1.X - b*d2.X + c*d3.X = p2.X - p1.X
  • a*d1.Y - b*d2.Y + c*d3.Y = p2.Y - p1.Y
  • a*d1.Z - b*d2.Z + c*d3.Z = p2.Z - p1.Z

使用Gaussian elimination,我们得到a,b和c的值。

如果a和b都是正数,则交点的位置为

  • Vector3 position = ray1.Position + a*ray1.Direction;
  • Vector3 direction = c * d3; //direction.Length() is the distance

为方便起见,您可以将这些值作为Ray返回。

如果a或b为负数,这意味着计算出的最短距离落在一条(或两条)光线之后,因此应使用不同的方法来找到最短距离。对于平行的线(交叉积d1 x d2为零),此方法相同。

现在计算变得找到哪条光线(正方向)最接近另一条光线的位置(p1或p2)。为此,我们使用点积(向量投影到另一个向量)

Dot product

图例:

  • dP = p2 - p1

在计算d1 dot dP的点积之前,请确保将d1(或d2)规范化(Vector3.Normalize()) - 点积应用于单位向量。

现在,基于ray1上的投影因子(点的结果)(让我们称之为a2)和ray2上的投影因子,找到哪个是最短距离的问题。 1}}(让我们称之为b2)。

如果a2b2都是负数(光线的负面),则最短距离是从一个位置到另一个位置。如果一个是负方向,那么另一个是最短的。另外,它是两者中较短的一个。


工作代码:

public Ray FindShortestDistance(Ray ray1, Ray ray2)
{
    if (ray1.Position == ray2.Position) // same position - that is the point of intersection
        return new Ray(ray1.Position, Vector3.Zero);

    var d3 = Vector3.Cross(ray1.Direction, ray2.Direction);

    if (d3 != Vector3.Zero) // lines askew (non - parallel)
    {
        //d3 is a cross product of ray1.Direction (d1) and ray2.Direction(d2)
        //    that means d3 is perpendicular to both d1 and d2 (since it's not zero - we checked that)    
        //
        //If we would look at our lines from the direction where they seem parallel 
        //    (such projection must always exist for lines that are askew)
        //    we would see something like this
        //
        //   p1   a*d1
        //   +----------->x------
        //                |
        //                | c*d3
        //       p2  b*d2 v 
        //       +------->x----
        //
        //p1 and p2 are positions ray1.Position and ray2.Position - x marks the points of intersection.
        //    a, b and c are factors we multiply the direction vectors with (d1, d2, d3)
        //
        //From the illustration we can the shortest distance equation
        //    p1 + a*d1 + c*d3 = p2 + b*d2
        //
        //If we rearrange it so we have a b and c on the left:
        //    a*d1 - b*d2 + c*d3 = p2 - p1
        //
        //And since all of the know variables (d1, d2, d3, p2 and p1) have 3 coordinates (x,y,z)
        //    now we have a set of 3 linear equations with 3 variables.
        //   
        //    a * d1.X - b * d2.X + c * d3.X = p2.X - p1.X
        //    a * d1.Y - b * d2.Y + c * d3.Y = p2.Y - p1.Y
        //    a * d1.Z - b * d2.Z + c * d3.Z = p2.Z - p1.Z
        //
        //If we use matrices, it would be
        //    [d1.X  -d2.X  d3.X ]   [ a ]   [p2.X - p1.X]
        //    [d1.Y  -d2.Y  d3.Y ] * [ a ] = [p2.Y - p1.Y]
        //    [d1.Z  -d2.Z  d3.Z ]   [ a ]   [p2.Z - p1.Z]
        //
        //Or in short notation
        //
        //   [d1.X  -d2.X  d3.X | p2.X - p1.X]
        //   [d1.Y  -d2.Y  d3.Y | p2.Y - p1.Y]
        //   [d1.Z  -d2.Z  d3.Z | p2.Z - p1.Z]
        //
        //After Gaussian elimination, the last column will contain values a b and c

        float[] matrix = new float[12];

        matrix[0] = ray1.Direction.X;
        matrix[1] = -ray2.Direction.X;
        matrix[2] = d3.X;
        matrix[3] = ray2.Position.X - ray1.Position.X;

        matrix[4] = ray1.Direction.Y;
        matrix[5] = -ray2.Direction.Y;
        matrix[6] = d3.Y;
        matrix[7] = ray2.Position.Y - ray1.Position.Y;

        matrix[8] = ray1.Direction.Z;
        matrix[9] = -ray2.Direction.Z;
        matrix[10] = d3.Z;
        matrix[11] = ray2.Position.Z - ray1.Position.Z;

        var result = Solve(matrix, 3, 4);

        float a = result[3];
        float b = result[7];
        float c = result[11];

        if (a >= 0 && b >= 0) // normal shortest distance (between positive parts of the ray)
        {
            Vector3 position = ray1.Position + a * ray1.Direction;
            Vector3 direction = d3 * c;

            return new Ray(position, direction);
        }
        //else will fall through below:
        //    the shortest distance was between a negative part of a ray (or both rays)
        //    this means the shortest distance is between one of the ray positions and another ray 
        //    (or between the two positions)
    }

    //We're looking for the distance between a point and a ray, so we use dot products now
    //Projecting the difference between positions (dP) onto the direction vectors will
    //   give us the position of the shortest distance ray.
    //The magnitude of the shortest distance ray is the the difference between its 
    //    position and the other rays position

    ray1.Direction.Normalize(); //needed for dot product - it works with unit vectors
    ray2.Direction.Normalize();

    Vector3 dP = ray2.Position - ray1.Position;

    //shortest distance ray position would be ray1.Position + a2 * ray1.Direction
    //                                     or ray2.Position + b2 * ray2.Direction (if b2 < a2)
    //                                     or just distance between points if both (a and b) < 0
    //if either a or b (but not both) are negative, then the shortest is with the other one
    float a2 = Vector3.Dot(ray1.Direction, dP);
    float b2 = Vector3.Dot(ray2.Direction, -dP);

    if (a2 < 0 && b2 < 0)
        return new Ray(ray1.Position, dP);


    Vector3 p3a = ray1.Position + a2 * ray1.Direction;
    Vector3 d3a = ray2.Position - p3a;

    Vector3 p3b = ray1.Position;
    Vector3 d3b = ray2.Position + b2 * ray2.Direction - p3b;

    if (b2 < 0)
        return new Ray(p3a, d3a);

    if (a2 < 0)
        return new Ray(p3b, d3b);

    if (d3a.Length() <= d3b.Length())
        return new Ray(p3a, d3a);

    return new Ray(p3b, d3b);
}

//Solves a set of linear equations using Gaussian elimination
float[] Solve(float[] matrix, int rows, int cols)
{
    for (int i = 0; i < cols - 1; i++)
        for (int j = i; j < rows; j++)
            if (matrix[i + j * cols] != 0)
            {
                if (i != j)
                    for (int k = i; k < cols; k++)
                    {
                        float temp = matrix[k + j * cols];
                        matrix[k + j * cols] = matrix[k + i * cols];
                        matrix[k + i * cols] = temp;
                    }

                j = i;

                for (int v = 0; v < rows; v++)
                    if (v == j)
                        continue;
                    else
                    {
                        float factor = matrix[i + v * cols] / matrix[i + j * cols];
                        matrix[i + v * cols] = 0;

                        for (int u = i + 1; u < cols; u++)
                        {
                            matrix[u + v * cols] -= factor * matrix[u + j * cols];
                            matrix[u + j * cols] /= matrix[i + j * cols];
                        }
                        matrix[i + j * cols] = 1;
                    }
                break;
            }

    return matrix;
}

答案 1 :(得分:-1)

这很简单。你需要应用AAS Triangle公式。

将上图视为三角形, Ray1位置为A,Ray2位置为B,您需要找到点c。在Ray AB和AC(alpha),BA和BC(θ)之间使用点积查找角度。 找到位置A和B(D)之间的距离。所以最后你有alpha,beta和D,即(三角形和三角形的一边)。应用AAS方法并找到C位置。

https://www.mathsisfun.com/algebra/trig-solving-aas-triangles.html