Có bao nhiêu và trục nào được sử dụng để va chạm 3D OBB với SAT


29

Tôi đã thực hiện SAT dựa trên:

Trên trang 7, trong bảng, nó đề cập đến trục 15 để kiểm tra để chúng ta có thể tìm thấy một vụ va chạm, nhưng chỉ với Axe, Ay và Az, tôi đã bị va chạm.

Tại sao tôi cần phải kiểm tra tất cả các trường hợp khác? Có tình huống nào mà chỉ Ax, Ay và Az không đủ?

Câu trả lời:


56

Bạn có thể nhận được dương tính giả. Va chạm phát hiện nhưng không thực sự va chạm.

Số 15 đến từ

  • 3 trục từ đối tượng A (mặt chuẩn)
  • 3 trục từ đối tượng B (mặt chuẩn)
  • 9 trục từ tất cả các cặp cạnh của A và các cạnh của B (3x3)
  • = 15 trong tổng số

9 trục được tạo thành từ các sản phẩm chéo của các cạnh của A và các cạnh của B

  1. Ae1 x Be1 (Cạnh 1 của A cạnh 1 của B)
  2. Ae1 x Be2
  3. Ae1 x Be3
  4. Ae2 x Be1
  5. ... và cứ thế

6 trục đầu tiên (từ các quy tắc khuôn mặt) được sử dụng để kiểm tra xem có một góc của một đối tượng đang giao nhau với một mặt của đối tượng khác hay không. (hoặc chính xác hơn để loại bỏ các loại va chạm này)

Tập hợp 9 trục được hình thành bởi các sản phẩm chéo của các cạnh được sử dụng để xem xét cạnh phát hiện va chạm cạnh, trong đó không có một đỉnh xuyên qua đối tượng khác. Giống như vụ va chạm 'gần như' trong bức ảnh dưới đây. Giả sử cho phần còn lại của câu trả lời này rằng hai hộp trong ảnh không thực sự va chạm, nhưng cách nhau một khoảng nhỏ.

nhập mô tả hình ảnh ở đây

Hãy nhìn vào những gì sẽ xảy ra nếu chúng ta chỉ sử dụng 6 quy tắc khuôn mặt cho SAT. Hình ảnh đầu tiên bên dưới hiển thị một trục từ hộp màu xanh và 2 trục từ hộp màu vàng. Nếu chúng ta chiếu cả hai đối tượng lên các trục này, chúng ta sẽ có sự chồng chéo lên cả ba. Hình ảnh thứ hai bên dưới cho thấy hai trục còn lại của hộp màu xanh và trục còn lại của hộp màu vàng. Một lần nữa chiếu vào các trục này sẽ hiển thị chồng lấp trên cả 3.

Vì vậy, chỉ cần kiểm tra 6 quy tắc khuôn mặt sẽ hiển thị chồng lấp trên tất cả 6 trục, theo SAT, có nghĩa là các đối tượng đang va chạm, vì chúng ta không thể tìm thấy sự phân tách. Nhưng tất nhiên, những vật thể này không va chạm. Lý do chúng tôi không tìm thấy một sự tách biệt là vì chúng tôi đã không nhìn đủ cứng!

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây

Vậy làm thế nào để chúng ta tìm thấy khoảng cách này? Hình ảnh dưới đây cho thấy một trục mà trên đó chiếu của cả hai đối tượng sẽ tiết lộ một sự tách biệt.

nhập mô tả hình ảnh ở đây

Chúng ta lấy trục này từ đâu?

Nếu bạn tưởng tượng trượt một mảnh thẻ cứng vào khoảng trống, thẻ đó sẽ là một phần của mặt phẳng phân tách. Nếu chúng ta chiếu vào bình thường của mặt phẳng đó (mũi tên đen trong hình trên), chúng ta sẽ thấy sự phân tách. Chúng ta biết mặt phẳng đó là gì bởi vì chúng ta có hai vectơ nằm trên mặt phẳng đó) Một vectơ được căn với cạnh màu xanh và vectơ kia được căn với cạnh màu vàng và như chúng ta đều biết bình thường đối với một mặt phẳng chỉ đơn giản là tích chéo của hai vectơ nằm trên mặt phẳng.

Vì vậy, đối với các OOBB, chúng ta cần kiểm tra mọi sự kết hợp (9 trong số đó) các sản phẩm chéo của các cạnh của hai đối tượng để đảm bảo chúng ta không bỏ sót bất kỳ sự tách biệt cạnh-cạnh nào.


2
Giải thích tuyệt vời! Và cảm ơn cho những hình ảnh. Như @Acegikmo lưu ý, sẽ hơi khó hiểu khi bạn nói "9 trục được tạo thành từ các sản phẩm chéo của các cạnh A và các cạnh của B", vì chúng ta chỉ có thể sử dụng các quy tắc, thay vì các cạnh. Cảm ơn một lần nữa :)

5
@joeRocc Đối với các khối lập phương bạn đúng, chỉ cần sử dụng các quy tắc, bởi vì các quy tắc và các cạnh được căn chỉnh, nhưng đối với các hình dạng khác (ví dụ tứ diện, các khối đa diện khác) các quy tắc không được căn chỉnh với các cạnh.
Ken

Cám ơn nhiều, ông bạn! Tôi đã đọc cuốn sách hay này có tên "Phát triển công cụ vật lý trò chơi" và tình cờ gặp vấn đề này. Không biết tại sao chúng ta sử dụng 15 trục. Cảm ơn rất nhiều. Bây giờ tôi đủ tự tin để khoe khoang về nó. ; D
Ankit singh kushwah

11

Ghi chú câu trả lời của Ken :

9 trục được tạo thành từ các sản phẩm chéo của các cạnh của A và các cạnh của B

Thật khó hiểu khi đề cập đến các cạnh, vì có 12 cạnh so với 6 quy tắc, khi bạn cũng có thể sử dụng ba quy tắc chính cho cùng một đầu ra - các cạnh đều được căn chỉnh với các quy tắc, vì vậy tôi khuyên bạn nên sử dụng chúng thay thế !

Cũng lưu ý rằng các quy tắc chỉ dọc theo cùng một trục, nhưng theo một hướng khác, sẽ bị bỏ qua và do đó chúng ta còn lại ba trục duy nhất.

Một điều nữa tôi muốn thêm vào, đó là bạn có thể tối ưu hóa tính toán này bằng cách thoát sớm nếu bạn tìm thấy một trục tách, trước khi bạn tính toán tất cả các trục bạn muốn kiểm tra. Vì vậy, không, bạn không cần phải kiểm tra tất cả các trục trong mọi trường hợp, nhưng bạn cần phải sẵn sàng để kiểm tra tất cả :)

Dưới đây là danh sách đầy đủ các trục để kiểm tra, đưa ra hai OBB, A và B, trong đó x, y và z tham chiếu đến các vectơ cơ sở / ba quy tắc duy nhất. Trục 0 = x, trục 1 = y, trục 2 = z

  1. a0
  2. a1
  3. a2
  4. b0
  5. b1
  6. b2
  7. chéo (a0, b0)
  8. chéo (a0, b1)
  9. chéo (a0, b2)
  10. chéo (a1, b0)
  11. chéo (a1, b1)
  12. chéo (a1, b2)
  13. chéo (a2, b0)
  14. chéo (a2, b1)
  15. chéo (a2, b2)

Cũng có một chút cảnh báo, mà bạn nên biết.

Sản phẩm chéo sẽ cung cấp cho bạn một vectơ không {0,0,0} khi bất kỳ hai trục giữa các đối tượng cùng hướng.

Ngoài ra, vì phần này bị bỏ qua, đây là phần triển khai của tôi để kiểm tra xem hình chiếu có bị chồng chéo hay không. Có lẽ có một cách tốt hơn, nhưng điều này làm việc cho tôi! (Sử dụng Unity và API C # của nó)

// aCorn and bCorn are arrays containing all corners (vertices) of the two OBBs
private static bool IntersectsWhenProjected( Vector3[] aCorn, Vector3[] bCorn, Vector3 axis ) {

    // Handles the cross product = {0,0,0} case
    if( axis == Vector3.zero ) 
        return true;

    float aMin = float.MaxValue;
    float aMax = float.MinValue;
    float bMin = float.MaxValue;
    float bMax = float.MinValue;

    // Define two intervals, a and b. Calculate their min and max values
    for( int i = 0; i < 8; i++ ) {
        float aDist = Vector3.Dot( aCorn[i], axis );
        aMin = ( aDist < aMin ) ? aDist : aMin;
        aMax = ( aDist > aMax ) ? aDist : aMax;
        float bDist = Vector3.Dot( bCorn[i], axis );
        bMin = ( bDist < bMin ) ? bDist : bMin;
        bMax = ( bDist > bMax ) ? bDist : bMax;
    }

    // One-dimensional intersection test between a and b
    float longSpan = Mathf.Max( aMax, bMax ) - Mathf.Min( aMin, bMin );
    float sumSpan = aMax - aMin + bMax - bMin;
    return longSpan < sumSpan; // Change this to <= if you want the case were they are touching but not overlapping, to count as an intersection
}

1
Chào mừng đến với trang web. Vui lòng kiểm tra trung tâm trợ giúp và đặc biệt lưu ý rằng trang web này không phải là một diễn đàn và việc "trả lời" các câu trả lời khác không phải là một ý tưởng hay (vì "trả lời" của bạn có thể không xuất hiện trước bài đăng bạn đang trả lời). Tốt hơn là viết câu trả lời của bạn theo cách độc lập và sử dụng nhận xét nếu bạn đặc biệt muốn làm rõ một bài đăng hiện có.
Josh

Cảm ơn đã làm rõ Acegikmo! Tôi đã có một chút bối rối bởi các tham chiếu đến các cạnh quá. @Josh Petrie bạn nên đặt mặt cười ở cuối bình luận để người mới biết bạn sẽ không tắt chúng :)

xem nhận xét của tôi ở trên cạnh cạnh so với thông thường
Ken

2

ví dụ làm việc c # dựa trên câu trả lời của Acegikmo (sử dụng một số api đoàn kết):

using UnityEngine;

public class ObbTest : MonoBehaviour
{
 public Transform A;
 public Transform B;

 void Start()
 {
      Debug.Log(Intersects(ToObb(A), ToObb(B)));
 }

 static Obb ToObb(Transform t)
 {
      return new Obb(t.position, t.localScale, t.rotation);
 }

 class Obb
 {
      public readonly Vector3[] Vertices;
      public readonly Vector3 Right;
      public readonly Vector3 Up;
      public readonly Vector3 Forward;

      public Obb(Vector3 center, Vector3 size, Quaternion rotation)
      {
           var max = size / 2;
           var min = -max;

           Vertices = new[]
           {
                center + rotation * min,
                center + rotation * new Vector3(max.x, min.y, min.z),
                center + rotation * new Vector3(min.x, max.y, min.z),
                center + rotation * new Vector3(max.x, max.y, min.z),
                center + rotation * new Vector3(min.x, min.y, max.z),
                center + rotation * new Vector3(max.x, min.y, max.z),
                center + rotation * new Vector3(min.x, max.y, max.z),
                center + rotation * max,
           };

           Right = rotation * Vector3.right;
           Up = rotation * Vector3.up;
           Forward = rotation * Vector3.forward;
      }
 }

 static bool Intersects(Obb a, Obb b)
 {
      if (Separated(a.Vertices, b.Vertices, a.Right))
           return false;
      if (Separated(a.Vertices, b.Vertices, a.Up))
           return false;
      if (Separated(a.Vertices, b.Vertices, a.Forward))
           return false;

      if (Separated(a.Vertices, b.Vertices, b.Right))
           return false;
      if (Separated(a.Vertices, b.Vertices, b.Up))
           return false;
      if (Separated(a.Vertices, b.Vertices, b.Forward))
           return false;

      if (Separated(a.Vertices, b.Vertices, Vector3.Cross(a.Right, b.Right)))
           return false;
      if (Separated(a.Vertices, b.Vertices, Vector3.Cross(a.Right, b.Up)))
           return false;
      if (Separated(a.Vertices, b.Vertices, Vector3.Cross(a.Right, b.Forward)))
           return false;

      if (Separated(a.Vertices, b.Vertices, Vector3.Cross(a.Up, b.Right)))
           return false;
      if (Separated(a.Vertices, b.Vertices, Vector3.Cross(a.Up, b.Up)))
           return false;
      if (Separated(a.Vertices, b.Vertices, Vector3.Cross(a.Up, b.Forward)))
           return false;

      if (Separated(a.Vertices, b.Vertices, Vector3.Cross(a.Forward, b.Right)))
           return false;
      if (Separated(a.Vertices, b.Vertices, Vector3.Cross(a.Forward, b.Up)))
           return false;
      if (Separated(a.Vertices, b.Vertices, Vector3.Cross(a.Forward, b.Forward)))
           return false;

      return true;
 }

 static bool Separated(Vector3[] vertsA, Vector3[] vertsB, Vector3 axis)
 {
      // Handles the cross product = {0,0,0} case
      if (axis == Vector3.zero)
           return false;

      var aMin = float.MaxValue;
      var aMax = float.MinValue;
      var bMin = float.MaxValue;
      var bMax = float.MinValue;

      // Define two intervals, a and b. Calculate their min and max values
      for (var i = 0; i < 8; i++)
      {
           var aDist = Vector3.Dot(vertsA[i], axis);
           aMin = aDist < aMin ? aDist : aMin;
           aMax = aDist > aMax ? aDist : aMax;
           var bDist = Vector3.Dot(vertsB[i], axis);
           bMin = bDist < bMin ? bDist : bMin;
           bMax = bDist > bMax ? bDist : bMax;
      }

      // One-dimensional intersection test between a and b
      var longSpan = Mathf.Max(aMax, bMax) - Mathf.Min(aMin, bMin);
      var sumSpan = aMax - aMin + bMax - bMin;
      return longSpan >= sumSpan; // > to treat touching as intersection
 }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.