Processing math: 100%

2011-12-09

AABB

Czyli Axis-Aligned Bound Box. Czyli prostopadłościan umieszczony w przestrzeni 3D o bokach równoległych do osi układu współrzędnych. Zanim przystąpimy do znajdowania intersekcji dla dowolnie skomplikowanego obiektu, bardziej opłacalne czasowo może być dla nas sprawdzanie intersekcji właśnie z jakąś bryłą w której ten obiekt zawiera się, w tym wypadku AABB. AABB są dosyć często używane gdyż badanie intersekcji z nimi jest dosyć szybkie. Są też naturalnym sojusznikiem dla podziału przestrzeni KD-Tree (zgodnie z płaszczyznami układu współrzędnych). Z uwagi na to, że AABB z reguły niezbyt ciasno opinają obiekt możliwa jest duża liczba fałszywych sprawdzeń. Wyliczanie AABB jest wolne, dla każdej transformacji obiektu trzeba je liczyć na nowo. W przeciwieństwie do np. OBB gdzie wystarczy OBB transformować wraz z obiektem. OOB zreguły opina nasz obiekt bardziej niż AABB. Zamiast mówić opina możemy wprowadzić miarę stosunku objętości bryły wpisanej w AABB do objętości AABB.

Do ich reprezentacji wykorzystujemy dwa punkty w przestrzeni: Minimum i Maksimum, gdzie Min <= Max dla każdej ze współrzędnych X, Y, Z.

Intersekcje z promieniem sprawdzamy osobno dla współrzędnych X, Y, Z. Zacznijmy od X. Sprawdzamy dla jakich t promień dany w postaci kierunkowej przecina płaszczyzny wyznaczone przez Min.X i Max.X.

x=START.X+DIR.XtMin.X=START.X+DIR.XttMIN=Min.XSTART.XDIR.XtMAX=Max.XSTART.XDIR.X

Teraz wyznaczamy dwie wartości które będą nam towarzyszyć poprzez cały test:

Far=Max(tMIN,tMAX)Near=Min(tMIN,tMAX)

Jeśli Far<0 to przecięcie nastąpiło przed punktem startu promienia. Tym samym intersekcji brak. Zauważmy, że Near może być ujemne.

Jeśli współrzędna kierunkowa DIR.X=0 to wtedy intersekcja jest możliwa jeśli Min.XSTART.XMax.X.

Przechodzimy do następnej płaszczyzny Y. Jeśli DIR.Y=0 to sprawdzamy czy START.Y leży w zakresie Min.YSTART.YMax.Y. Przeciwnie wyznaczamy podobnie jak poprzednio tMIN i tMAX. Wyznaczamy:

Far1=Max(tMIN,tMAX)
Near1=Min(tMIN,tMAX)

I teraz wyliczamy nowa Near i Far:

Far=Min(Far,Far1)
Near=Max(Near,Near1)

Pamiętajmy, że Far i Near to tak naprawdę parametr t promienia. Wyznaczamy więc dwukrotnie wartości parametru t dla którego prosta przecina dane płaszczyzny. Mamy dwie wartości Near i Far z testu dla X i teraz mamy dwie nowe wartości z testy dla Y. Jeśli prosta przetnie AABB to dokona tego dla konkretnych dwóch wartości t. Podstawiając je do wzorów na Near i Far dla konkretnych płaszczyzn powinniśmy otrzymać współrzędne Min i Max. W zakresie t pomiędzy punktem wejścia w AABB, a wyjścia współrzędne punktów prostej leżą pomiędzy Min i Max. Powyższe wyliczenia znajdują nowy wspólny zbiór parametrów t dla których prosta znajduje się jednocześnie pomiędzy Min.X i Max.X oraz pomiędzy Min.Y, a Max.X.

Jeśli Far<0 to intersekcji brak. Zbiór t dla której są one pomiędzy Min.Y, a Max.Y leży przed punktem startu promienia.

Powyższy rysunek obrazuje różne kombinacje możliwych intersekcji:


  
    
      
    
  
  
  
    
      
        image/svg+xml
        
        
      
    
  
  
    
    
    
    
    
    
      
      
    
    
      
      
    
    
      
      
    
    
      
      
    
    
      
      
    
    
    
    MIN.X
    MAX.X
    MIN.Y
    MAX.Y
    1
    2
    3
    
    5
    
    
    
    
    
    
      
      
    
    
    6
  

Dla promienia 2 współrzędna kierunkowa DIR.Y. Ponieważ nie zachodzi Min.YSTART.YMax.Y prosta ta nigdy nie przetnie AABB. W przeciwieństwie do promienia 5. Dla promienia 1 zbiory <Near.X,Far.X> oraz <Near.Y,Far.Y> są rozłączne. Promień ten nigdy nie przetnie AABB. W przeciwieństwie do promienia 4, gdzie zbiory te mają część wspólną. Promień 3 jest przykładem promienia dla którego Near<0 utrzymuje się przez cały czas obliczeń. I tak ujemna wartość zostanie zwrócona jako odległość do miejsca intersekcji. Wartość oznaczająca brak intersekcji to Double.PositiveInfinity. Wartość ujemna oznacza, że punkt startu promienia znajduje się w AABB. Taki wynik oznacza, że powinniśmy także przetestować na intersekcje wszystkie obiekty w tym AABB.

Jeśli Far<Near to intersekcji brak. Zbiory t są rozłączne, innymi słowy prosta nigdy jednocześnie nie znajduje się pomiędzy Min.X i Max.X oraz Min.Y i Max.Y i tym samym nie może przeciąć AABB.

Podobnie jak dla Y czynimy dla Z.

Ostatecznie otrzymuje segment promienia który przecina AABB, z reguły interesuje nas tylko współrzędna wejścia do AABB.

Kod wyznaczania dystansu do punktu pierwszego punktu intersekcji:
public double GetDist(Ray a_ray)
{
    Debug.Assert(Minimum <= Maximum);

    double n = Double.MinValue;
    double f = Double.MaxValue;

    if (a_ray.Dir.X.AlmostRelativeEquals(0))
    {
        if ((a_ray.Start.X <= Minimum.X) || (a_ray.Start.X >= Maximum.X))
            return Double.PositiveInfinity;
    }
    else
    {
        double d1 = (Minimum.X - a_ray.Start.X) / a_ray.Dir.X;
        double d2 = (Maximum.X - a_ray.Start.X) / a_ray.Dir.X;

        f = Math.Max(d1, d2);

        if (f < 0)
            return double.PositiveInfinity;

        n = Math.Min(d1, d2);
    }

    if (a_ray.Dir.Y.AlmostRelativeEquals(0))
    {
        if ((a_ray.Start.Y <= Minimum.Y) || (a_ray.Start.Y >= Maximum.Y))
            return Double.PositiveInfinity;
    }
    else
    {
        double d1 = (Minimum.Y - a_ray.Start.Y) / a_ray.Dir.Y;
        double d2 = (Maximum.Y - a_ray.Start.Y) / a_ray.Dir.Y;

        f = Math.Min(f, Math.Max(d1, d2));

        if (f < 0)
            return double.PositiveInfinity;

        n = Math.Max(n, Math.Min(d1, d2));

        if (n > f)
            return Double.PositiveInfinity;
    }

    if (a_ray.Dir.Z.AlmostRelativeEquals(0))
    {
        if ((a_ray.Start.Z <= Minimum.Z) || (a_ray.Start.Z >= Maximum.Z))
            return Double.PositiveInfinity;
    }
    else
    {
        double d1 = (Minimum.Z - a_ray.Start.Z) / a_ray.Dir.Z;
        double d2 = (Maximum.Z - a_ray.Start.Z) / a_ray.Dir.Z;

        f = Math.Min(f, Math.Max(d1, d2));

        if (f < 0)
            return double.PositiveInfinity;

        n = Math.Max(n, Math.Min(d1, d2));

        if (n > f)
            return Double.PositiveInfinity;
    }

    return n;
}

Brak komentarzy:

Prześlij komentarz