2010-05-27 61 views
9

Một phương pháp rõ ràng để tính toán khoảng cách tối thiểu từ điểm đến tam giác 3D là chiếu điểm lên mặt phẳng của tam giác, xác định tọa độ barycentric của điểm kết quả và sử dụng chúng để xác định điểm dự kiến ​​nằm trong Tam giác. Nếu không, hãy kẹp tọa độ barycentric của nó nằm trong khoảng [0,1], và nó sẽ cho bạn điểm gần nhất nằm bên trong tam giác.Cách nhanh nhất để tính điểm đến khoảng cách tam giác trong 3D?

Có cách nào để tăng tốc độ này hoặc đơn giản hóa nó bằng cách nào đó không?

+2

kẹp tọa barycentric không cho chiếu trực giao với mép gần nhất và do đó không phải là khoảng cách đúng nếu một điểm trên các cạnh (không có đỉnh) là gần nhất. –

Trả lời

13

Có nhiều cách tiếp cận khác nhau để tìm khoảng cách từ điểm P0 đến tam giác P1, P2, P3.

  1. Phương pháp 3D. Chiếu điểm lên mặt phẳng của tam giác và sử dụng tọa độ barycentric hoặc một số phương tiện khác để tìm điểm gần nhất trong tam giác. Khoảng cách được tìm thấy theo cách thông thường.

  2. Phương pháp 2D. Áp dụng một bản dịch/xoay sang các điểm sao cho P1 nằm trên nguồn gốc, P2 nằm trên trục z, P3 trong mặt phẳng yz. Chiếu là điểm P0 là tầm thường (bỏ bê tọa độ x). Điều này dẫn đến một vấn đề 2D. Sử dụng phương trình cạnh có thể xác định đỉnh hoặc cạnh gần nhất của tam giác. Tính toán khoảng cách là sau đó dễ dàng-peasy.

Điều này paper so sánh hiệu suất của cả hai với phương pháp 2D.

+0

Tôi không đồng ý với kết luận rằng bạn cần 4 căn bậc hai để tính khoảng cách đến tam giác khi làm việc với 3D nếu bạn định khung chính xác truy vấn của mình. Tất cả có thể được thực hiện với các sản phẩm chéo và dấu chấm. – bradgonesurfing

6

Giả sử bạn đang sử dụng một trong các thuật toán nhanh đã biết, cách duy nhất để tăng tốc là khi bạn đang thực hiện rất nhiều phép đo trên nhiều hình tam giác. Trong trường hợp đó, bạn có thể giữ được rất nhiều số lượng được đặt trước trong cấu trúc "cạnh" hoặc "uốn lượn". Thay vì lưu trữ 3 điểm, bạn lưu trữ mắt lưới bao gồm các cấu trúc cạnh. Phép chiếu sau đó trở thành các phép thử rất nhanh và có thể được mã hóa sao cho chúng là có thể dự đoán chi tiết.

Điều quan trọng là giữ mọi thứ trong bộ nhớ cache. Bộ vi xử lý có thể thực hiện MUL và DIV trong gần 1 chu kỳ đồng hồ để bộ nhớ thường là nút cổ chai.

Ngoài ra, hãy cân nhắc viết bản ngã trong SSE3 hoặc thứ gì đó tương tự (chẳng hạn như Mono's SIMD support). Đó là công việc, nhưng bạn thường có thể làm một vài hình tam giác tại một thời điểm nếu bạn nghĩ rằng đủ cứng về nó.

Tôi sẽ cố gắng tìm một số giấy tờ về chủ đề này, nhưng bạn có thể muốn Google cho "Giao lộ Ray". Điều đó sẽ mang lại tất cả những công việc tuyệt vời từ những năm 80 và 90 khi mọi người làm việc chăm chỉ để tối ưu hóa công cụ này.

+0

"Bộ vi xử lý có thể làm ... DIV trong gần 1 chu kỳ đồng hồ nên bộ nhớ thường là nút cổ chai." - có thật không? Con số cuối cùng tôi nghe được gần 17 chu kỳ. –

1

Tôi sẽ dẫn đầu với kết quả thử nghiệm của mình.

enter image description here

Các trường hợp kiểm tra mã và thực hiện là trong C#

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 ); 

    } 

Mã thực hiện là khó sử dụng như tôi có một số khuôn khổ lớp học. Hy vọng rằng bạn có thể coi đây là mã giả và rút ra thuật toán. Các loại véc tơ thô là từ https://www.nuget.org/packages/System.DoubleNumerics/.

Lưu ý rằng một số thuộc tính của Tam giác có thể được lưu trong bộ nhớ cache để cải thiện hiệu suất.

Lưu ý rằng để trả lại điểm gần nhất không yêu cầu bất kỳ căn bậc hai nào và không yêu cầu chuyển sự cố sang 2D.

Thuật toán trước tiên sẽ nhanh chóng kiểm tra nếu điểm kiểm tra gần nhất với vùng điểm kết thúc. Nếu đó là không thuyết phục nó sau đó kiểm tra các khu vực cạnh bên ngoài từng người một. Nếu những kiểm tra đó thất bại thì điểm nằm trong tam giác. Lưu ý rằng đối với các điểm được chọn ngẫu nhiên cách xa tam giác, rất có thể điểm gần nhất sẽ là điểm góc của tam giác.

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); 

    } 
} 

Và cấu trúc cạnh

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; 

} 

Và cấu trúc máy bay

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; 

} 
Các vấn đề liên quan