2012-02-01

Superellipsoid

Superellipsoida jest bryłą zamkniętą o powierzchni, która nie zawsze jest ciągła (zależnie od parametrów).

Równanie Superellipsoidy w postatci uwikłanej:

$ \left( \left|x\right|^{R} + \left|y\right|^{R} \right)^{\frac{T}{R}} + \left|z\right|^{T} = 1$

, gdzie $T \in \mathbb{R}$ i $R \in \mathbb{R}$

Bryła jest zamknięta w sześcianie (-1, 1). Bryła jest symetryczna względem płaszczyzn układu współrzędnych. Punkt znajduje się we wnętrzu bryły jeśli:

$ \left( \left|x\right|^{R} + \left|y\right|^{R} \right)^{\frac{T}{R}} + \left|z\right|^{T} < 1$

Gradient z funkcji uwikłanej definiującej powierzchnię:

$\displaystyle\frac{df(x,y,z)}{dx} = T\cdot\left(\left|x\right|^R+\left|y\right|^R\right)^{\frac{T}{R}-1}\cdot sign(x) \cdot \left|x\right|^{R-1}$

$\displaystyle\frac{df(x,y,z)}{dx} = T\cdot\left(\left|x\right|^R+\left|y\right|^R\right)^{\frac{T}{R}-1}\cdot sign(x) \cdot \left|x\right|^{R-1}$

$\displaystyle\frac{df(x,y,z)}{dx} = T\cdot\left(\left|x\right|^R+\left|y\right|^R\right)^{\frac{T}{R}-1}\cdot sign(y) \cdot \left|y\right|^{R-1}$


$\displaystyle\frac{df(x,y,z)}{dx} = T \cdot sign(z) \cdot \left|z\right|^{T-1}$

Gradient dla punktu powierzchni bryły jest równy normalnej w tym punkcie.

Równanie parametryczne ma postać:

$\begin{align} x(u,v) &{}= A c\left(v,\frac{2}{t}\right) c\left(u,\frac{2}{r}\right) \\ y(u,v) &{}= B c\left(v,\frac{2}{t}\right) s\left(u,\frac{2}{r}\right) \\ z(u,v) &{}= C s\left(v,\frac{2}{t}\right) \\ & -\pi/2 \le v \le \pi/2, \quad -\pi \le u < \pi ,\end{align}$

,gdzie:

$\begin{align} c(\omega,m) &{}= \text{sign}(\cos \omega) |\cos \omega|^m \\ s(\omega,m) &{}= \text{sign}(\sin \omega) |\sin \omega|^m\end{align}$

Kod generujący z tego równania siatkę w Matlabie:

function [ ] = plot_se( )

R = 2.3;
T = 0.5;
scale = [1 1 1];

n = 200;
etamax = pi / 2;
etamin = -pi / 2;
wmax = pi;
wmin = -pi;
delta = (etamax - etamin) / n;
dw = (wmax - wmin) / n;
[i,j] = meshgrid(1:n+1, 1:n+1);
eta = etamin + (i-1) * delta;
w = wmin + (j-1) * dw;
x = scale(1) .* sign(cos(eta)) .* abs(cos(eta)).^(2/T) .* ...
sign(cos(w)) .* abs(cos(w)).^(2/R);
y = scale(2) .* sign(cos(eta)) .* abs(cos(eta)).^(2/T) .* ...
sign(sin(w)) .* abs(sin(w)).^(2/R);
z = scale(3) .* sign(sin(eta)) .* abs(sin(eta)).^(2/T);

z = z - 2;

meshc(x,y,z);
title(sprintf('R: %.1f, T: %.1f', R, T));

end


Przykłady:



Cały kod jest podobny do kodu dla SuperQuadric. Zmieniamy tylko zawartość funkcji Norm i Gradient i zmieniay kod tak by zamiast Exponent wykorzystywane był dwie zmienne T i R.

Kod:

public class SuperellipsoidObject : RenderableObject
{
    private RootFinder m_root_finder;
    private double m_R = 1;
    private double m_T = 1;
    private double m_TR = 1;

    private double Norm(Vector3 a_point)
    {
        return Math.Pow(Math.Pow(Math.Abs(a_point.X), R) + 
            Math.Pow(Math.Abs(a_point.Y), R), TR) + 
            Math.Pow(Math.Abs(a_point.Z), T);
    }

    [YAXNode]
    public double R
    {
        get
        {
            return m_R;
        }
        set
        {
            m_R = value;
            m_TR = T / R;
        }
    }

    [YAXNode]
    public double T
    {
        get
        {
            return m_T;
        }
        set
        {
            m_T = value;
            m_TR = T / R;
        }
    }

    private double TR
    {
        get
        {
            return m_TR;
        }
    }

    public SuperellipsoidObject(Vector3 a_right, Vector3 a_up) :
        base(a_right, a_up)
    {
        Name = "Superellipsoid";
        Closed = true;
        m_uv_mapper = new SphericalUVMapper();
        Update(UpdateFlags.All);
    }

    private Vector3 Gradient(Vector3 a_point)
    {
        double ax = Math.Abs(a_point.X);
        double ay = Math.Abs(a_point.Y);
        double xR1 = Math.Pow(ax, R - 1);
        double yR1 = Math.Pow(ay, R - 1);
        double xryr = Math.Pow(xR1 * ax + yR1 * ay, TR - 1);

        return new Vector3(
            xryr * Math.Sign(a_point.X) * T * xR1,
            xryr * Math.Sign(a_point.Y) * T * yR1, 
            Math.Sign(a_point.Z) * T * Math.Pow(Math.Abs(a_point.Z), T - 1));   
    }

    private double GetDistToNearestAxisOrOneOneBoxPlaneAndInsideOneOneBox(
        Vector3 a_ray_start, Vector3 a_ray_dir)
    {
        double dist_min = Double.PositiveInfinity;

        double dist = -(a_ray_start.Z / a_ray_dir.Z);
        if (dist &gt; 0)
        {
            Vector3 p = (a_ray_start + a_ray_dir * dist);
            if (p.Abs() &lt;= Vector3.ONE)
                dist_min = dist;
        }

        dist = -(a_ray_start.X / a_ray_dir.X);
        if ((dist &gt; 0) &amp;&amp; (dist &lt; dist_min))
        {
            Vector3 p = (a_ray_start + a_ray_dir * dist);
            if (p.Abs() &lt;= Vector3.ONE)
                dist_min = dist;
        }

        dist = -(a_ray_start.Y / a_ray_dir.Y);
        if ((dist &gt; 0) &amp;&amp; (dist &lt; dist_min))
        {
            Vector3 p = (a_ray_start + a_ray_dir * dist);
            if (p.Abs() &lt;= Vector3.ONE)
                dist_min = dist;
        }

        dist = Plane.GetDist(a_ray_start, a_ray_dir, Vector3.XAXIS, Vector3.XAXIS);
        if ((dist &gt; 0) &amp;&amp; (dist &lt; dist_min))
        {
            Vector3 p = (a_ray_start + a_ray_dir * dist);
            if (p.Abs().IsAlmostLessThen(Vector3.ONE))
                dist_min = dist;
        }

        dist = Plane.GetDist(a_ray_start, a_ray_dir, Vector3.XAXIS, -Vector3.XAXIS);
        if ((dist &gt; 0) &amp;&amp; (dist &lt; dist_min))
        {
            Vector3 p = (a_ray_start + a_ray_dir * dist);
            if (p.Abs().IsAlmostLessThen(Vector3.ONE))
                dist_min = dist;
        }

        dist = Plane.GetDist(a_ray_start, a_ray_dir, Vector3.YAXIS, Vector3.YAXIS);
        if ((dist &gt; 0) &amp;&amp; (dist &lt; dist_min))
        {
            Vector3 p = (a_ray_start + a_ray_dir * dist);
            if (p.Abs().IsAlmostLessThen(Vector3.ONE))
                dist_min = dist;
        }

        dist = Plane.GetDist(a_ray_start, a_ray_dir, Vector3.YAXIS, -Vector3.YAXIS);
        if ((dist &gt; 0) &amp;&amp; (dist &lt; dist_min))
        {
            Vector3 p = (a_ray_start + a_ray_dir * dist);
            if (p.Abs().IsAlmostLessThen(Vector3.ONE))
                dist_min = dist;
        }

        dist = Plane.GetDist(a_ray_start, a_ray_dir, Vector3.ZAXIS, Vector3.ZAXIS);
        if ((dist &gt; 0) &amp;&amp; (dist &lt; dist_min))
        {
            Vector3 p = (a_ray_start + a_ray_dir * dist);
            if (p.Abs().IsAlmostLessThen(Vector3.ONE))
                dist_min = dist;
        }

        dist = Plane.GetDist(a_ray_start, a_ray_dir, Vector3.ZAXIS, -Vector3.ZAXIS);
        if ((dist &gt; 0) &amp;&amp; (dist &lt; dist_min))
        {
            Vector3 p = (a_ray_start + a_ray_dir * dist);
            if (p.Abs().IsAlmostLessThen(Vector3.ONE))
                dist_min = dist;
        }

        return dist_min;
    }

    private double GetMinimalDistanceFromRayToSurface(Vector3 a_ray_start, 
        Vector3 a_ray_dir, double a_a, double a_b)
    {
        bool start_inside = Inside(a_ray_start);
        bool inside = start_inside;
        double min_t = Double.PositiveInfinity;

        for (int i = 0; i &lt; Constants.ROOT_FINDER_MAXIMUM_ITERATIONS; i++)
        {
            if (Math.Abs(a_a - a_b) &lt; Constants.ROOT_FINDER_ABSOLUTE_ERROR)
                break;

            min_t = (a_a + a_b) / 2;
            Vector3 point = a_ray_start + a_ray_dir * min_t;
            Vector3 mid_grad = Gradient(point);

            double norm = Norm(point);

            if (start_inside)
            {
                if (norm &gt; 1)
                {
                    inside = false;
                    break;
                }
            }
            else
            {
                if (norm &lt; 1)
                {
                    inside = true;
                    break;
                }
            }

            double mid_dot = mid_grad * a_ray_dir;

            if (mid_dot &lt; 0)
                a_a = min_t;
            else
                a_b = min_t;
        }

        if (inside == start_inside)
            return Double.PositiveInfinity;

        return min_t;
    }

    private bool Inside(Vector3 a_local_point)
    {
        return !Norm(a_local_point).IsAlmostGreaterThen(1);
    }

    internal override Intersection GetIntersection(
        Intersection a_source_ray_intersection, Ray a_ray)
    {
        Vector3 local_dir;
        Vector3 local_start;
        double near_t;
        double far_t;

        TransformToLocal(a_ray, out local_dir, out local_start);

        if (!GetLocalBoundBox().GetIntersections(
            local_start, local_dir, out near_t, out far_t))
        {
            return Scene.NoIntersection;
        }

        Func<double, double=""> f = (t) =&gt; Norm(local_start + local_dir * t) - 1;

        bool back_hit = false;

        if (near_t &lt; 0)
        {
            if (Inside(local_start))
                back_hit = true;
            if (Norm(local_start).IsAlmostEquals(1))
            {
                Vector3 normal = -Gradient(local_start).Normalized;
                if (normal * local_dir &gt; 0)
                    back_hit = true;

                local_start = local_start + local_dir * Constants.MINIMAL_DISTANT;
                far_t -= Constants.MINIMAL_DISTANT;
                near_t -= Constants.MINIMAL_DISTANT;
            }
        }

        if (back_hit &amp;&amp; OneSide)
            return Scene.NoIntersection;

        if (near_t &gt; 0)
        {
            local_start = local_start + local_dir * 
                (near_t - Constants.MINIMAL_DISTANT * 2);
            far_t = far_t - (near_t - Constants.MINIMAL_DISTANT * 2);
            near_t = Constants.MINIMAL_DISTANT * 2;
        }

        double local_dist = Double.PositiveInfinity;

        for (; ; )
        {
            double plane_t = GetDistToNearestAxisOrOneOneBoxPlaneAndInsideOneOneBox(
                local_start, local_dir);

            if (plane_t == Double.PositiveInfinity)
                break;

            double min_t = GetMinimalDistanceFromRayToSurface(
                local_start, local_dir, 0, plane_t);

            if (min_t != Double.PositiveInfinity)
            {
                local_dist = m_root_finder.FindRoot(0, min_t, f);

                if (local_dist == Double.PositiveInfinity)
                    local_dist = m_root_finder.FindRoot(min_t, plane_t, f);
            }
            else
                local_dist = m_root_finder.FindRoot(0, plane_t, f);

            if (local_dist != Double.PositiveInfinity)
                break;

            local_start = local_start + local_dir * plane_t;
            far_t -= plane_t;
        }

        if (local_dist == Double.PositiveInfinity)
            return Scene.NoIntersection;

        Vector3 local_pos = local_start + local_dir * local_dist;
        Vector3 world_pos = LocalToWorld * local_pos;
        double world_dist = (world_pos - a_ray.Start).Length;

        Vector3 local_norm = -Gradient(local_pos).Normalized;

        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 &gt;= 0)
            return Scene.NoIntersection;

        return intersection;
    }

    protected override AABB GetLocalBoundBox()
    {
        return new AABB(-Vector3.ONE, Vector3.ONE);
    }

    internal override Vector3 GetNormal(Intersection a_intersection)
    {
        if (a_intersection.BackHit)
        {
            return (LocalToWorldNormal * 
                -Gradient(a_intersection.LocalPos)).Normalized;
        }
        else
        {
            return (LocalToWorldNormal * 
                Gradient(a_intersection.LocalPos)).Normalized;
        }
    }

    internal override Vector3 GetUVW(Intersection a_intersection)
    {
        return base.GetUVW(a_intersection) * 0.5 + Vector3.HALF;
    }

    public override string ToString()
    {
        return String.Format("Superellipsoid: {0}, R: {1}, T: {2}", Name, R, T);
    }

    internal override void GetTangents(Intersection a_intersection, 
        out Vector3 a_tangent_x, out Vector3 a_tangent_y)
    {
        a_tangent_x = Vector3.CrossProduct(Up, a_intersection.Pos).Normalized;

        if (a_intersection.BackHit)
        {
            a_tangent_y = Vector3.CrossProduct(a_intersection.Normal, 
                a_tangent_x).Normalized;
        }
        else
        {
            a_tangent_y = Vector3.CrossProduct(a_tangent_x, 
                a_intersection.Normal).Normalized;
        }
    }

    protected override void RenderStart(RenderStartPhase a_phase)
    {
        base.RenderStart(a_phase);

        if (a_phase == RenderStartPhase.PrepareObjectToRender)
        {
            m_root_finder = RootFinder.Create(
                Scene.RenderOptions.RootFindingMethod);
        }
    }

    public override void ScaleAbsolute(double a_scale)
    {
        Scale *= a_scale;

        base.ScaleAbsolute(a_scale);
    }
}

Przykłady:

R=2.3, T=0.5


R=0.5, T=0.5

Brak komentarzy:

Prześlij komentarz