Processing math: 100%

2011-12-09

Torus - równanie i intersekcja z promieniem

Mały promień torusa oznaczmy przez r, duży przez R.


  
  
  
    
      
        image/svg+xml
        
        
      
    
  
  
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    r
    r
    R
    R
  

Równanie okręgu: x2+y2=r2, którego obrót tworzy torus przesuniętego o R to: (xR)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+z2R)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+z2R)2+y2=r2
x2+z22Rx2+z2+R2+y2=r2
x2+y2+z2+R2r2=2Rx2+z2
(x2+y2+z2+R2r2)2=4R2(x2+z2)

Podstawiając do powyższego parametryczne równanie linii o punkcie startu S i kierunku D:

P=S+Dt
x=xs+xdt
y=ys+ydt
z=zs+zdt

, otrzymujemy:

(DDt2+2DSt+SS+R2r2)2=4R2((DDydyd)t2+2(DSydys)t+SSysys)

, ponieważ wektor D jest znormalizowany to:


(t2+2DSt+SS+R2r2)2=4R2((1ydyd)t2+2(DSydys)t+SSysys)

, po oznaczeniu:

β=2SD
γ=SS+R2r2

, otrzymujemy:

(t2+βt+γ)2=4R2((1ydyd)t2+(β2ydys)t+SSysys)

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