Oznaczenia na rysunkach:
→d - kierunek promienia, zakładamy, że wektor ten jest znormalizowany
S - punkt wyjścia promienia
O - środek sfery
A - punkt w którym prosta promienia i promień sfery przecinają się pod kątem prostym
c=‖→SO‖
Oba przypadki wymagają osobnego rozpatrzenia.
Przypadek 1
Warunek: c>rMamy pewność, że punkt S leży na zewnątrz sfery. Teraz sprawdźmy, czy wogóle zmierza w jej kierunku. Badamy znak →d→c, który jest tożsamy ze znakiem cosinusa kąt pomiędzy tymi wektorami. Jeśli →d⋅→SO<=0 to promień nigdy nie przetnie się ze sferą. Długość wektora →v będącego rzutem wektora →c na →d. Ponieważ wektor →d jest znormalizowany to
v=→d⋅→SO
c2+h2=v2
h=√c2−v2
√c2−v2<r
Teraz pozostaje nam wyznaczyć punkt przecięcia. m2+h2=r2
m=√r2−h2
m=√r2−(c2−v2)
k=v−m
P=S+→d⋅k
m=√r2−h2
m=√r2−(c2−v2)
k=v−m
P=S+→d⋅k
Przypadek 2
Warunek: c<r
Mamy pewność, że punkt S leży wewnątrz sfery. Podobnie wyliczamy:
v=→d→c
v2+h2=c2
h=√c2−v2
m2+h2=r2
m=√r2−h2
m=√r2−(c2−v2)
I teraz inaczej niż poprzednio:v2+h2=c2
h=√c2−v2
m2+h2=r2
m=√r2−h2
m=√r2−(c2−v2)
k=v+m
I znów podobnie: P=S+→d⋅k
Ponowne uderzenie w sfere przez promień odbity albo załamay
Pozostaje do wyjaśnienia przypadek c≈r - ponieważ operujemy na liczbach zmiennoprzecinkowych. Istnieją dwa sposoby radzenia sobie z sytuacją. W pierwszym przesuwamy punkt startu wzdłuż wektora →d o niewielką wartość, która pozwala nam przezwyciężyć błąd obliczeniowy. Niestety o ile przy odbiciu od zewnętrznej części sfery promienia metoda ta działa dobrze, o ile przy odbiciu wewnętrznym może ona nam w skrajnych przypadkach przesunąć punkt startu na zewnątrz sfery, co jest ewidentnym błędem. Poza tym ustalenie wartości przesunięcia też jest dosyć problematyczne. Musi być ściśle zgrane z odstępami pomiędzy obiektami i ich wymiarami. Wymiary obiektów ani odstępy pomiędzy nimi nie mogą być mniejsze niż pewna graniczna wartość tego przesunięcia. Samo przesunięcie musi być większe niż błąd obliczeniowy i tutaj jest też duży problem jeśli chcemy by wartość ta była naprawdę minimalna.
W drugiej metodzie pamiętamy w co i z której strony ostatnio uderzyliśmy. Dzięki temu dokładnie kontrolujemy sytuację. Jeśli uderzenie nastąpiło z zewnątrz to promień odbity nie uderzy ponownie w sferę. Jeśli uderzenie nastąpiło od środka, to promień odbity może uderzyć w sferę. Całkiem podobnie jest dla promienia załamanego.
Przykład implementacji w C#
public override Intersection GetIntersection(Ray a_ray) { bool backHit = false; Vector3 c = Pos - a_ray.Start; double c2 = c.Length * c.Length; double r2 = Radius * Radius; double dist; if (a_ray.PrevHit == this) { if (a_ray.PrevBackHit) { backHit = true; if (OneSide) return Scene.NoIntersection; else { double v = c * a_ray.Dir; double m2 = r2 - (c2 - v * v); dist = v + Math.Sqrt(m2); } } else return Scene.NoIntersection; } else { double v = c * a_ray.Dir; double m2 = r2 - (c2 - v * v); if (c2 > r2) { if (v <= 0) return Scene.NoIntersection; else { if (m2 < 0) return Scene.NoIntersection; else dist = v - Math.Sqrt(m2); } } else { backHit = true; if (OneSide) return Scene.NoIntersection; else dist = v + Math.Sqrt(m2); } } return new Intersection() { SceneObject = this, Ray = a_ray, Dist = dist, Scene = Scene, BackHit = backHit, Pos = a_ray.HitPoint(dist) }; }
Brak komentarzy:
Prześlij komentarz