Równanie okręgu: x2+y2=r2, którego obrót tworzy torus przesuniętego o R to: (x−R)2+y2=r2
Utwórzmy torus poprzez obrót tego okręgu wokół osi Y. Jeśli wyobrazimy sobie taki torus i przetniemy go dowolną płaszczyzną przechodzącą przez oś Y to nasz torus jest definiowany przez podane wyżej równanie 2D. Zauważmy, że x w nim w przypadku 3D oznacza: √(x2+z2), czyli równanie torusa w 3D ma postać:
(√x2+z2−R)2+y2=r2
W zależności od stosunku R do r możemy otrzymać torusy o różnym kształcie. W tym sferę dla R=0 (w zasadzie dwie nakładające się na siebie sfery). Okręg w 3D dla r=0. Torus z dziurką dla R>r i torusy bez dziurki dla r>R. Dla takich przypadków wraz jak r staje się większe od R to nasz torus coraz bardziej upodabnia się do sfery, a wgłębienia zanikają.
Równanie to możemy zapisać jako:
(√x2+z2−R)2+y2=r2
x2+z2−2R√x2+z2+R2+y2=r2
x2+y2+z2+R2−r2=2R√x2+z2
(x2+y2+z2+R2−r2)2=4R2(x2+z2)
Podstawiając do powyższego parametryczne równanie linii o punkcie startu S i kierunku D:
P=S+→D⋅t
x=xs+xdt
y=ys+ydt
z=zs+zdt
, otrzymujemy:
(D⋅D⋅t2+2⋅D⋅S⋅t+S⋅S+R2−r2)2=4R2((D⋅D−ydyd)⋅t2+2⋅(D⋅S−ydys)⋅t+S⋅S−ysys)
, ponieważ wektor →D jest znormalizowany to:
(t2+2⋅D⋅S⋅t+S⋅S+R2−r2)2=4R2((1−ydyd)⋅t2+2⋅(D⋅S−ydys)⋅t+S⋅S−ysys)
, po oznaczeniu:
β=2⋅S⋅D
γ=S⋅S+R2−r2
, otrzymujemy:
(t2+βt+γ)2=4R2((1−ydyd)⋅t2+(β−2ydys)⋅t+S⋅S−ysys)
Stąd już możemy łatwo wyliczyć potrzebne nam współczynniki do rozwiązania wielomianu stopnia czwartego:
At4+Bt3+Ct2+Dt+E=0
A=1B = 2 \betaC = \beta ^ 2 + 2 \gamma + (2 R \cdot D.Y)^2D = 2 \beta \gamma + 8 R^2 \cdot S.Y \cdot D.YE = \gamma ^ 2 + (2 R \cdot S.Y)^2 - (2 R r)^2Równanietojestznormalizowane,tzn.A=1$. Z czterech możliwych rozwiązań rzeczywistych liczy się dla nas to najmniejsze, ale większe od zera, a dokładniej większej od minimalnej odległości pomiędzy obiektami
Constants.MINIMAL_DISTANCE
.Teraz musimy jeszcze ustalić w którą stronę torusa uderzył promień. W tym celu najlepiej wyliczyć normalną i sprawdzić kąt pomiędzy nią, a promieniem. O tym jak ją liczyć będzie w innym poście. Tak samo jak o metodach rozwiązywania wielomianów.
Przykładowy kod:
public override Intersection GetIntersection( Intersection a_source_ray_intersection, Ray a_ray) { Vector3 local_dir = default(Vector3); Vector3 local_start = default(Vector3); bool back_hit = false; if (!TransformToLocalToBoundBox(a_ray, ref local_dir, ref local_start)) return Scene.NoIntersection; double r2 = m_local_small_radius * m_local_small_radius; double R2 = m_local_big_radius * m_local_big_radius; double beta = 2 * local_dir * local_start; double gamma = local_start.SqrLen - r2 - R2; double b = 2 * beta; double c = beta * beta + 2 * gamma + 4 * R2 * local_dir.Y * local_dir.Y; double d = 2 * beta * gamma + 8 * R2 * local_start.Y * local_dir.Y; double e = gamma * gamma + 4 * R2 * local_start.Y * local_start.Y - 4 * R2 * r2; double dist = m_solver.SolveQuartic(1, b, c, d, e); if (dist == Double.PositiveInfinity) return Scene.NoIntersection; Vector3 local_pos = local_start + local_dir * dist; Vector3 normal = (local_pos - new Vector3(local_pos.X, 0, local_pos.Z).Normalized * m_local_big_radius).Normalized; back_hit = normal * local_dir > 0; if (back_hit && OneSide) return Scene.NoIntersection; Vector3 world_pos = LocalToWorld * local_pos; double world_dist = (world_pos - a_ray.Start).Length; Intersection intersection = new Intersection() { PrevIntersection = a_source_ray_intersection, SceneObject = this, SourceRay = a_ray, LocalPos = local_pos, Dist = world_dist, Scene = Scene, BackHit = back_hit, Pos = world_pos }; // Numerical errors. if (intersection.Normal * intersection.SourceRay.Dir >= 0) return Scene.NoIntersection; return intersection; }
Brak komentarzy:
Prześlij komentarz