首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在3D中计算点到三角形距离的最快方法?

在3D中计算点到三角形距离的最快方法?
EN

Stack Overflow用户
提问于 2010-05-28 04:48:19
回答 4查看 30.3K关注 0票数 15

计算点到三维三角形的最小距离的一种显而易见的方法是将点投影到三角形的平面上,确定生成点的重心坐标,并使用它们来确定投影点是否位于三角形内。如果不是,则将其重心坐标限制在0,1范围内,这将为您提供位于三角形内的最近点。

有没有办法以某种方式加速或简化它?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2010-05-28 06:16:07

有不同的方法可以求出点P0到三角形P1,P2,P3的距离。

  1. 3D方法。将该点投影到三角形的平面上,并使用重心坐标或某些其他方法来查找三角形中最近的点。距离是用通常的方法求出的。
  2. 2D方法。对点应用平移/旋转,使P1在原点上,P2在z轴上,P3在yz平面上。投影是点P0是微不足道的(忽略x坐标)。这将导致2D问题。使用边方程,可以确定三角形中最近的顶点或边。这样计算距离就很容易了。

这个paper将两者的性能与2D方法的优胜进行了比较。

票数 12
EN

Stack Overflow用户

发布于 2017-11-27 15:33:32

我将主导我的测试用例结果。

测试用例代码和实现是用C#编写的

代码语言:javascript
运行
复制
    public void ClosestPointToShouldWork()
    {
        var r = new Random(0);
        double next() => r.NextDouble() * 5 - 1;
        var t = new Triangle(new Vector3(0,0,0), new Vector3(3.5,2,0), new Vector3(3,0.0,0));

        DrawTriangle(t);

        var hash = new Vector3( 0, 0, 0 );
        for (int i = 0; i < 800; i++)
        {
            var pt = new Vector3( next(), next(), 0 );
            var pc = t.ClosestPointTo( pt );
            hash += pc;

            DrawLine(pc,pt);
        }

        // Test the hash
        // If it doesn't match then eyeball the visualization
        // and see what has gone wrong

        hash.ShouldBeApproximately( new Vector3(1496.28118561104,618.196568578824,0),1e-5  );

    }

实现代码很繁琐,因为我有许多框架类。希望你能把它当做伪代码来处理,并提取出算法。原始的向量类型来自https://www.nuget.org/packages/System.DoubleNumerics/

请注意,Triangle的一些属性可以缓存以提高性能。

请注意,返回最近点不需要任何平方根,也不需要将问题转换为2D问题。

该算法首先快速测试测试点是否最接近终点区域。如果没有结果,它就会逐个测试边缘外部区域。如果这些测试失败,则点在三角形内。请注意,对于远离三角形的随机选定点,最接近的点很可能是三角形的角点。

代码语言:javascript
运行
复制
public class Triangle
{
    public Vector3 A => EdgeAb.A;
    public Vector3 B => EdgeBc.A;
    public Vector3 C => EdgeCa.A;

    public readonly Edge3 EdgeAb;
    public readonly Edge3 EdgeBc;
    public readonly Edge3 EdgeCa;

    public Triangle(Vector3 a, Vector3 b, Vector3 c)
    {
        EdgeAb = new Edge3( a, b );
        EdgeBc = new Edge3( b, c );
        EdgeCa = new Edge3( c, a );
        TriNorm = Vector3.Cross(a - b, a - c);
    }

    public Vector3[] Verticies => new[] {A, B, C};

    public readonly Vector3 TriNorm;

    private static readonly RangeDouble ZeroToOne = new RangeDouble(0,1);

    public Plane TriPlane => new Plane(A, TriNorm);

    // The below three could be pre-calculated to
    // trade off space vs time

    public Plane PlaneAb => new Plane(EdgeAb.A, Vector3.Cross(TriNorm, EdgeAb.Delta  ));
    public Plane PlaneBc => new Plane(EdgeBc.A, Vector3.Cross(TriNorm, EdgeBc.Delta  ));
    public Plane PlaneCa => new Plane(EdgeCa.A, Vector3.Cross(TriNorm, EdgeCa.Delta  ));

    public static readonly  RangeDouble Zero1 = new RangeDouble(0,1);

    public Vector3 ClosestPointTo(Vector3 p)
    {
        // Find the projection of the point onto the edge

        var uab = EdgeAb.Project( p );
        var uca = EdgeCa.Project( p );

        if (uca > 1 && uab < 0)
            return A;

        var ubc = EdgeBc.Project( p );

        if (uab > 1 && ubc < 0)
            return B;

        if (ubc > 1 && uca < 0)
            return C;

        if (ZeroToOne.Contains( uab ) && !PlaneAb.IsAbove( p ))
            return EdgeAb.PointAt( uab );

        if (ZeroToOne.Contains( ubc ) && !PlaneBc.IsAbove( p ))
            return EdgeBc.PointAt( ubc );

        if (ZeroToOne.Contains( uca ) && !PlaneCa.IsAbove( p ))
            return EdgeCa.PointAt( uca );

        // The closest point is in the triangle so 
        // project to the plane to find it
        return TriPlane.Project( p );

    }
}

和边缘结构

代码语言:javascript
运行
复制
public struct Edge3
{

    public readonly Vector3 A;
    public readonly Vector3 B;
    public readonly Vector3 Delta;

    public Edge3(Vector3 a, Vector3 b)
    {
        A = a;
        B = b;
        Delta = b -a;
    }

    public Vector3 PointAt(double t) => A + t * Delta;
    public double LengthSquared => Delta.LengthSquared();

    public double Project(Vector3 p) => (p - A).Dot( Delta ) / LengthSquared;

}

和平面结构

代码语言:javascript
运行
复制
public struct Plane
{
    public Vector3 Point;
    public Vector3 Direction;

    public Plane(Vector3 point, Vector3 direction )
    {
            Point = point;
            Direction = direction;
    }

    public bool IsAbove(Vector3 q) => Direction.Dot(q - Point) > 0;

}
票数 7
EN

Stack Overflow用户

发布于 2010-05-28 04:57:08

假设您使用的是一种已知的快速算法,那么提高速度的唯一方法是当您对许多三角形进行大量测量时。在这种情况下,您可以在“边缘”或“缠绕”结构中保留大量预先计算的量。不是存储这3个点,而是存储由边结构组成的网格。然后,投影变得非常迅速,重心测试可以被编码为branch-predictable.

真正的关键是将所有内容都放在缓存中。处理器可以在近1个时钟周期内执行MUL和DIV,因此内存通常是瓶颈。

此外,可以考虑用SSE3或类似的语言(如Mono's SIMD support)编写算法。这是工作,但你通常可以一次做几个三角形,如果你仔细考虑的话。

我会试着找一些关于这个主题的论文,但你可能想在谷歌上搜索"Ray Mesh Intersection“。这将带来80年代和90年代以来的所有伟大工作,当时人们努力优化这些东西。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2924795

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档