SAT ile 3D OBB çarpışması için kaç tane ve hangi eksen kullanılacak


29

SAT'ı aşağıdakiler temelinde uyguluyorum:

7. sayfada, tabloda, bir çarpışma bulabilmemiz için test edilecek 15 ekseni ifade ediyor, ancak sadece Ax, Ay ve Az ile çarpışma alıyorum.

Diğer tüm vakaları neden test etmem gerekiyor? Sadece Ax, Ay ve Az’ın yeterli olmadığı durumlar var mı?

Yanıtlar:


56

Yanlış pozitifler alıyor olabilirsiniz. Çarpışmalar tespit edildi ancak gerçekte çarpışmıyor.

15 numara geliyor

  • A nesnesinden 3 eksen (yüz normalleri)
  • B nesnesinden 3 eksen (yüz normalleri)
  • Tüm A çiftleri ve B (3x3) kenarlarından 9 eksen
  • = Toplamda 15

9 eksen, A kenarlarının ve B kenarlarının çapraz ürünlerinden oluşur.

  1. Ae1 x Be1 (B'nin A 1 kenarının 1 kenarı)
  2. Ae1 x Be2
  3. Ae1 x Be3
  4. Ae2 x Be1
  5. ... ve bunun gibi

İlk 6 eksen (yüz normallerinden), bir nesnenin köşesinin, diğer nesnenin bir yüzüyle kesişip kesiştiğini kontrol etmek için kullanılır. (veya bu tür çarpışmaları gidermek için daha doğru)

Kenarların çapraz ürünleri tarafından oluşturulan 9 eksen takımı, diğer nesneye nüfuz eden bir tepe noktasının olmadığı kenar çarpışma algılamasında kenarı dikkate almak için kullanılır. Aşağıdaki fotoğraftaki 'neredeyse' çarpışma gibi. Bu cevabın geri kalan kısmında, resimdeki iki kutunun aslında çarpışmadığını, ancak küçük bir mesafeyle ayrıldığını varsayalım.

görüntü tanımını buraya girin

SAT için 6 normal normal kullanırsak neler olacağına bakalım. Aşağıdaki ilk resim mavi kutudan bir eksen ve sarı kutudan 2 eksen göstermektedir. Her iki nesneyi de bu eksenlere yansıtırsak, üçünün üzerinde de üst üste bineriz. Aşağıdaki ikinci resim mavi kutunun kalan iki eksenini ve sarı kutunun kalan eksenini göstermektedir. Yine bu eksenlere yansıtma, 3'ün hepsinde de örtüşmeler gösterecektir.

Bu yüzden sadece 6 yüz normunu kontrol etmek, 6 eksenin hepsinde örtüşmeler gösterecektir; bu, SAT'a göre nesnelerin çarpışmakta olduğu anlamına gelir, çünkü bir ayrım bulamadık. Ama elbette, bu nesne çarpışmıyor. Ayrılık bulamamamızın nedeni, yeterince sert görünmememizdir!

görüntü tanımını buraya girin görüntü tanımını buraya girin

Peki bu boşluğu nasıl bulacağız? Aşağıdaki resim, her iki nesnenin projeksiyonunun bir ayrım ortaya çıkaracağı ekseni göstermektedir.

görüntü tanımını buraya girin

Bu ekseni nereden alıyoruz?

Bir parça sert kartın boşluğa kaydırılmasını hayal ediyorsanız, bu kart ayırma düzleminin bir parçası olacaktır. Eğer o düzlemin normaline yansıtırsak (yukarıdaki resimde siyah ok), ayrımı göreceğiz. O düzlemin ne olduğunu biliyoruz, çünkü o düzlemde yatan iki vektörümüz var) Bir vektör mavi kenara, diğer vektör sarıya doğru hizalanır ve hepimizin bildiği gibi normal bir düzlem uçakta yatan iki vektör çapraz ürün.

Bu nedenle, OOBB'ler için, herhangi bir kenar kenarı ayrımını kaçırmadığımızdan emin olmak için iki nesnenin kenarlarının çapraz ürünlerinin her bir kombinasyonunu (9 tanesini) kontrol etmemiz gerekir.


2
Müthiş açıklama! Ve resimler için teşekkürler. @Acegikmo'nun belirttiği gibi, "9 eksen A ve B'nin kenarlarının çapraz ürünlerinden oluşuyor" demeniz biraz kafa karıştırıcı çünkü biz sadece kenarları değil, normalleri kullanabiliriz. Tekrar teşekkürler :)

5
@joeRocc Cuboids için haklısın, sadece normalleri kullanın, çünkü normaller ve kenarlar aynı hizadadır, ancak diğer şekiller için (örneğin, tetrahedronlar, diğer polihedronlar) normaller kenarlarla hizalı değildir.
Ken

Çok teşekkürler dostum! “Game Physics engine development” adlı bu güzel kitabı okuyordum ve bu soruna rastladım. Neden 15 eksen kullandığımız hakkında hiçbir fikrim yoktu. Çok teşekkürler. Şimdi övünecek kadar kendime güveniyorum. ; D
Ankit singh kushwah

11

Ken'in cevap notları:

9 eksen, A kenarlarının ve B kenarlarının çapraz ürünlerinden oluşur.

Aynı çıkış için üç ana normu kullanabildiğiniz zaman, 6 normale kıyasla 12 kenar olduğundan, kenarlara atıfta bulunmak biraz kafa karıştırıcıdır - kenarların tümü normlarla aynı hizadadır; !

Ayrıca, aynı eksen boyunca, ancak farklı bir yöne işaret eden normallerin göz ardı edildiğine ve böylece üç benzersiz eksenle kaldığımızı da unutmayın.

Eklemek istediğim bir diğer şey, ayırma ekseni bulursanız, test etmek istediğiniz tüm eksenleri hesaplamadan önce, erken çıkarak bu hesaplamayı optimize edebilmenizdir. Yani, hayır, her durumda tüm eksenleri test etmeniz gerekmez, ancak hepsini test etmeye hazır olmanız gerekir :)

Burada, iki OBB, A ve B verildiğinde, x, y ve z'nin temel vektörleri / üç benzersiz normları gösterdiği test edilecek eksenlerin tam listesi. 0 = x ekseni, 1 = y ekseni, 2 = z ekseni

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

Dikkat etmeniz gereken küçük bir uyarı var.

Çapraz ürün, nesneler arasındaki herhangi iki eksen aynı yöne işaret ettiğinde size {0,0,0} sıfır vektörünü verecektir.

Ayrıca, bu bölüm dışarıda bırakıldığından, projeksiyonun çakışıp çakışmadığını kontrol etmek için benim uygulamam. Muhtemelen daha iyi bir yol var, ama bu benim için çalıştı! (Unity ve C # API'sini kullanma)

// 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
Siteye Hoşgeldiniz. Lütfen yardım merkezini inceleyin ve özellikle bu sitenin bir forum olmadığını ve diğer cevaplara "yanıtlamanın" iyi bir fikir olmadığını unutmayın (çünkü "cevabınız" yanıt verdiğiniz gönderiden önce görünmeyebilir). Cevaplarınızı bağımsız olarak yazmanız ve özellikle mevcut bir yazıyı netleştirmek istiyorsanız yorumları kullanmanız daha iyi olacaktır.
Josh

Açıklama Acegikmo için teşekkürler! Ben de kenarlardan yapılan referansla biraz kafam karıştı. @Josh Petrie sen :) onları kapatmadan değildir yenilere sizi öğrenmesi için yorumların sonunda gülümsemeleri koymalıyız

yukarıdaki yorumumu re re vs vs normals
Ken

2

Acegikmo'nun cevabına dayanan c # örneği (bazı birlik api'leri kullanarak):

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
 }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.