【问题标题】:Intersection between a line and a sphere直线和球体的交点
【发布时间】:2011-05-04 12:13:51
【问题描述】:

我正在尝试找到球体和线之间的交点,但老实说,我不知道该怎么做。 谁能帮我解决这个问题?

【问题讨论】:

  • 查找“光线球相交”——在光线追踪中一直使用相同的测试,网上有很多例子,甚至在 stackoverflow 上还有quite a few here

标签: math geometry intersection


【解决方案1】:

将该行表示为t的函数:

{ x(t) = x0*(1-t) + t*x1
{ y(t) = y0*(1-t) + t*y1
{ z(t) = z0*(1-t) + t*z1

t = 0 时,它将在一个端点(x0,y0,z0)。当t = 1时,它将在另一个端点(x1,y1,z1)

t(其中(xc,yc,zc) 是球心)中写出到球心(平方)的距离公式:

f(t) = (x(t) - xc)^2 + (y(t) - yc)^2 + (z(t) - zc)^2

f(t) 等于R^2 时求解tR 是球体的半径):

(x(t) - xc)^2 + (y(t) - yc)^2 + (z(t) - zc)^2 = R^2

A = (x0-xc)^2 + (y0-yc)^2 + (z0-zc)^2 - R^2
B = (x1-xc)^2 + (y1-yc)^2 + (z1-zc)^2 - A - C - R^2
C = (x0-x1)^2 + (y0-y1)^2 + (z0-z1)^2

A + B*t + C*t^2 = 0 求解为t。这是一个普通的quadratic equation

您最多可以获得两个解决方案。 t 位于 0 和 1 之间的任何解决方案都是有效的。

如果您得到t 的有效解,请将其代入第一个方程以获得交点。

我假设您的意思是线段(两个端点)。如果您想要一条完整的线(无限长),那么您可以沿线选择两个点(不要太近),然后使用它们。也让t 成为任何实际值,而不仅仅是介于 0 和 1 之间。

编辑:我修正了B 的公式。我把标志弄混了。感谢 M Katz,提到它不起作用。

【讨论】:

    【解决方案2】:

    我认为 Markus Jarderot 的解决方案存在错误。不知道问题是什么,但我很确定我将它忠实地翻译成代码,当我试图找到一条已知穿过球体的线段的交点时,我得到了一个负判别式(没有解决方案)。

    我发现了这个:http://www.codeproject.com/Articles/19799/Simple-Ray-Tracing-in-C-Part-II-Triangles-Intersec,它给出了类似但略有不同的推导。

    我把它变成了下面的 C# 代码,它适用于我:

        public static Point3D[] FindLineSphereIntersections( Point3D linePoint0, Point3D linePoint1, Point3D circleCenter, double circleRadius )
        {
            // http://www.codeproject.com/Articles/19799/Simple-Ray-Tracing-in-C-Part-II-Triangles-Intersec
    
            double cx = circleCenter.X;
            double cy = circleCenter.Y;
            double cz = circleCenter.Z;
    
            double px = linePoint0.X;
            double py = linePoint0.Y;
            double pz = linePoint0.Z;
    
            double vx = linePoint1.X - px;
            double vy = linePoint1.Y - py;
            double vz = linePoint1.Z - pz;
    
            double A = vx * vx + vy * vy + vz * vz;
            double B = 2.0 * (px * vx + py * vy + pz * vz - vx * cx - vy * cy - vz * cz);
            double C = px * px - 2 * px * cx + cx * cx + py * py - 2 * py * cy + cy * cy +
                       pz * pz - 2 * pz * cz + cz * cz - circleRadius * circleRadius;
    
            // discriminant
            double D = B * B - 4 * A * C;
    
            if ( D < 0 )
            {
                return new Point3D[ 0 ];
            }
    
            double t1 = ( -B - Math.Sqrt ( D ) ) / ( 2.0 * A );
    
            Point3D solution1 = new Point3D( linePoint0.X * ( 1 - t1 ) + t1 * linePoint1.X,
                                             linePoint0.Y * ( 1 - t1 ) + t1 * linePoint1.Y,
                                             linePoint0.Z * ( 1 - t1 ) + t1 * linePoint1.Z );
            if ( D == 0 )
            {
                return new Point3D[] { solution1 };
            }
    
            double t2 = ( -B + Math.Sqrt( D ) ) / ( 2.0 * A );
            Point3D solution2 = new Point3D( linePoint0.X * ( 1 - t2 ) + t2 * linePoint1.X,
                                             linePoint0.Y * ( 1 - t2 ) + t2 * linePoint1.Y,
                                             linePoint0.Z * ( 1 - t2 ) + t2 * linePoint1.Z );
    
            // prefer a solution that's on the line segment itself
    
            if ( Math.Abs( t1 - 0.5 ) < Math.Abs( t2 - 0.5 ) )
            {
                return new Point3D[] { solution1, solution2 };
            }
    
            return new Point3D[] { solution2, solution1 };
        }
    

    【讨论】:

    • Bmy answer 的公式错误。我现在已经修好了。
    • 这对我来说非常有效,我也能够快速将其转换为 Java。
    • 如果您指出该代码与 GPLv3 相关联会很有帮助。
    【解决方案3】:

    没有足够的声誉来评论 M Katz 的答案,但他的答案假设这条线可以在每个方向上无限延伸。如果只需要线段的交点,则需要 t1 和 t2 小于 1(基于参数化方程的定义)。请在下面查看我在 C# 中的答案:

            public static Point3D[] FindLineSphereIntersections(Point3D linePoint0, Point3D linePoint1, Point3D circleCenter, double circleRadius)
        {
    
            double cx = circleCenter.X;
            double cy = circleCenter.Y;
            double cz = circleCenter.Z;
    
            double px = linePoint0.X;
            double py = linePoint0.Y;
            double pz = linePoint0.Z;
    
            double vx = linePoint1.X - px;
            double vy = linePoint1.Y - py;
            double vz = linePoint1.Z - pz;
    
            double A = vx * vx + vy * vy + vz * vz;
            double B = 2.0 * (px * vx + py * vy + pz * vz - vx * cx - vy * cy - vz * cz);
            double C = px * px - 2 * px * cx + cx * cx + py * py - 2 * py * cy + cy * cy +
                       pz * pz - 2 * pz * cz + cz * cz - circleRadius * circleRadius;
    
            // discriminant
            double D = B * B - 4 * A * C;
    
            double t1 = (-B - Math.Sqrt(D)) / (2.0 * A);
    
            Point3D solution1 = new Point3D(linePoint0.X * (1 - t1) + t1 * linePoint1.X,
                                             linePoint0.Y * (1 - t1) + t1 * linePoint1.Y,
                                             linePoint0.Z * (1 - t1) + t1 * linePoint1.Z);
    
            double t2 = (-B + Math.Sqrt(D)) / (2.0 * A);
            Point3D solution2 = new Point3D(linePoint0.X * (1 - t2) + t2 * linePoint1.X,
                                             linePoint0.Y * (1 - t2) + t2 * linePoint1.Y,
                                             linePoint0.Z * (1 - t2) + t2 * linePoint1.Z);
    
            if (D < 0 || t1 > 1 || t2 >1)
            {
                return new Point3D[0];
            }
            else if (D == 0)
            {
                return new [] { solution1 };
            }
            else
            {
                return new [] { solution1, solution2 };
            }
        }
    

    【讨论】:

    • 再评论,我的代码不完整——参数线的定义是 0 1 || t2 >1) 更改为 (D 1 || t1 1 || t2
    • 感谢您粘贴代码。不过,我可能在其中发现了一个错误。我正在查看从球体外部开始并进入球体的线段,但交点返回 null(空数组)。请参阅:paste.ofcode.org/aJSjXFpXvvRGf5hYcn8M9Q。非常感谢您对此发表任何评论,谢谢!
    【解决方案4】:

    您可以使用 Wolfram Alpha 在以球体为中心的坐标系中求解。

    在这个系统中,方程是:

    球体:

         x^2 + y^2 + z^2 = r^2  
    

    直线:

        x = x0 + Cos[x1] t
        y = y0 + Cos[y1] t
        z = z0 + Cos[z1] t 
    

    Then we ask Wolfram Alpha to solve for t:(试试看!)

    之后您可以再次更改为原始坐标系(简单的平移)

    【讨论】:

      【解决方案5】:

      求 (x,y,z) 中描述直线和球体的两个方程的解。

      可能有 0、1 或 2 个解决方案。

      • 0 表示它们不相交
      • 1 表示直线与球体相切
      • 2 表示直线穿过球体。

      【讨论】:

        【解决方案6】:

        这是一个更简洁的公式,它使用内积,少于 100 个 LOC,并且没有外部链接。此外,问题是针对一条线而不是线段提出的。

        假设球体以C 为中心,半径为r。该行由P+l*D 描述,其中D*D=1PC 是点,D 是向量,l 是数字。

        我们设置了PC = P-Cpd = PC*Ds = pd*pd - PC*PC + r*r。如果s &lt; 0 没有解决方案,如果s == 0 只有一个,否则有两个。对于我们设置l = -pd +- sqrt(s)的解决方案,然后插入P+l*D

        【讨论】:

          【解决方案7】:

          或者你可以找到两者的公式:
          line:(x-x0)/a=(y-y0)/b=(z-z0)/c,是你能找到的点之间线段的对称方程。
          球体:(x-xc)^2+(y-yc)^2+(z-zc)^2 = R^2.

          使用对称方程找出 x 和 y 以及 x 和 z 之间的关系。

          然后根据 x 将 y 和 z 代入球体方程。
          然后找到x,然后就可以找到y和z了。

          如果 x 给你一个假想的结果,这意味着直线和球体不相交。

          【讨论】:

            【解决方案8】:

            我没有评论 Ashavsky 解决方案的声誉,但最后的检查需要更多调整。

            if (D < 0)
                return new Point3D[0];
            else if ((t1 > 1 || t1 < 0) && (t2 > 1 || t2 < 0))
                return new Point3D[0];
            else if (!(t1 > 1 || t1 < 0) && (t2 > 1 || t2 < 0))
                return new [] { solution1 };
            else if ((t1 > 1 || t1 < 0) && !(t2 > 1 || t2 < 0))
                return new [] { solution2 };
            else if (D == 0)
                return new [] { solution1 };
            else
                return new [] { solution1, solution2 };
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2014-09-24
              • 1970-01-01
              • 1970-01-01
              • 2010-10-12
              • 1970-01-01
              • 2020-04-13
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多