2011-12-09

Torus - normalna

Normalną możemy policzyć korzystając z gradientu funkcji lub poprzez podejście geometryczne. Podejście geometryczne daje poprawne wyniki kiedy r>R i kiedy R=0.

Równanie torusa leżącego w płaszczyźnie XZ:

$(\sqrt{x^2+y^2}-R)^2+y^2=r^2$

Normalna to gradient funkcji:

$f(x,y,z)=(\sqrt{x^2+y^2}-R)^2+y^2-r^2$
$\nabla f=\left[2x-\frac{2xR}{\sqrt{x^2+z^2}},2y,2z-\frac{2zR}{\sqrt{x^2+z^2}}\right]$

Możemy także najpierw lekko uporządkować funkcje:

$(\sqrt{x^2+z^2}-R)^2+y^2=r^2$
$x^2+z^2-2R\sqrt{x^2+z^2}+R^2+y^2=r^2$
$x^2+y^2+z^2+R^2-r^2=2R\sqrt{x^2+z^2}$
$(x^2+y^2+z^2+R^2-r^2)^2=4R^2(x^2+z^2)$
$f(x,y,z)=(x^2+y^2+z^2+R^2-r^2)^2-4R^2(x^2+z^2)$
$\begin{align*}
\nabla f=[& 4x(x^2+y^2+z^2+R^2-r^2)^2-8xR^2, \\& 4y(x^2+y^2+z^2+R^2-r^2)^2, \\& 4z(x^2+y^2+z^2+R^2-r^2)^2-8zR^2]
\end{align*}$

W podejściu geometrycznym mamy punkt w który uderzył promień należący do torusa $P(x,y,z)$. Torus leży w płaszczyźnie $XZ$. Rzut wektora $\vec P$ (od środka torusa do punktu P) na płaszczyznę $XZ$ to $\vec A=[x,y,0]$. Wektor o długości $R$ o takim samym kierunku to $\vec B = R\hat A$. Wektor $\hat{\vec P-\vec B}$ to normalna. Jeśli mamy uderzenie od środka torusa to należy ją wziąć ze znakiem minus.

Kod wszystkich trzech wersji:
public override Vector3 GetNormal(
   Intersection a_intersection)
{
    Vector3 local_pos = a_intersection.LocalPos;
            
    //float R2 = m_local_big_radius * m_local_big_radius;
    //float n = local_pos.SqrLen + R2 - m_local_small_radius * m_local_small_radius;
    //        
    //Vector3 normal = new Vector3(
    //    local_pos.X * n - 2 * R2 * local_pos.X,
    //    local_pos.Y * n,
    //    local_pos.Z * n - 2 * R2 * local_pos.Z);
    //
    //if (a_intersection.BackHit)
    //    return (LocalToWorldNormal * -normal).Normalized;
    //else
    //    return (LocalToWorldNormal * normal).Normalized;

    if (a_intersection.BackHit)
    {
        return (LocalToWorldNormal * (new Vector3(local_pos.X, 0, 
                local_pos.Z).Normalized * m_local_big_radius - 
                local_pos)).Normalized;
    }
    else
    {
        return (LocalToWorldNormal * (local_pos - new Vector3(local_pos.X, 0, 
                local_pos.Z).Normalized * m_local_big_radius)).Normalized;
    }

    //var nn = LocalToWorldNormal * new Vector3(
    //    local_pos.X - local_pos.X * m_local_big_radius / 
    //    (float)Math.Sqrt(local_pos.X * local_pos.X + local_pos.Z * local_pos.Z),
    //    local_pos.Y,
    //    local_pos.Z - local_pos.Z * m_local_big_radius / 
    //    (float)Math.Sqrt(local_pos.X * local_pos.X + local_pos.Z * local_pos.Z)
    //    ).Normalized;
    //if (a_intersection.BackHit)
    //    return -nn;
    //else
    //    return nn;
}

Brak komentarzy:

Prześlij komentarz