2011-12-10

Mapowanie tekstury na stożek

Nazwijmy to mapowanie stożkowym.

Jeśli UVW.Y = 1 to mapowanie przeprowadzamy dla postawy: UV = [UVW.X, UVW.Y]. Przeciwnie mapowanie przeprowadzamy dla powierzchni bocznej stożka.

Jeśli boczną powierzchnię stożka rozwiniemy to otrzymamy wycinek koła. Jeśli wysokość stożka to H, zaś promień jego podstawy to R, to okrąg ten ma promień równy długości tworzącej stożek: $L = \sqrt{R^2 + H^2}$. Obwód stożka u podstawy to $2 \pi R$ i jest to tym samym długość łuku wycinka.

Pierwsze co potrzebujemy to miarę kąta pomiędzy płaszczyzną przechodzącą przez oś stożka i wektor Forward, a płaszczyzną wyznaczoną przez oś stożka i punkt na nim. W moim przypadku wartość tego kąta: $\alpha = \displaystyle\arctan \left ( {\frac{UVW.X-0.5}{-(UVW.Z-0.5)}} \right )$, gdzie $\alpha \subset \left\langle 0,2 \pi\right\rangle$.

Kąt wycinka okręgu, który tworzy bok stożka ma wartość:

$\displaystyle{\frac{\beta}{2 \pi L}=\frac{2 \pi}{2 \pi L}\Rightarrow \beta=\frac{2 \pi R}{L}=\frac{2 \pi R}{\sqrt{R^2 + H^2}}}$

Musimy przeskalować wartość kątu $\alpha$ do zakresu $\left\langle 0, \beta \right\rangle$. Przeskalowana wartość kąta $\alpha$ ma wartość:

$\displaystyle\frac{\alpha}{2 \pi L}=\frac{\alpha_1}{2 \pi R} \Rightarrow \alpha_1=\alpha \frac{R}{L}$

Wartość $\displaystyle S=\frac{R}{L}=\frac{R}{\sqrt{R^2 + H^2}}$ jest stała i można ją wyliczyć na starcie renderowania.

W moim przypadku odjąłem od kąta $\alpha_1$ wartość $\displaystyle\frac{\pi}{2}$ i dodałem $\displaystyle\frac{\beta}{2}=S\pi$ ustawiając początek startu nawijania na [u,v]=[0, 0.5]. Nawijanie odbywa się w prawo.

Mając tak przeliczony kąt:

$\displaystyle\alpha_2=\alpha_1 - \frac{\pi}{2} + S\pi$.

Teraz możemy wyliczyć koordynaty tekstur:

$u = 0.5 + 0.5 \cdot UVW.Y \cdot \cos\left(\alpha_2\right)$
$v = 0.5 + 0.5 \cdot UVW.Y \cdot \sin\left(\alpha_2\right)$

UVW.Y oznacza w tym wypadku odległość punku na powierzchni bocznej stożka od jego wierzchołka i zawiera się ona w granicach od zera do jeden.

Kod:

public class ConicalUVMapper : UVMapper
{
    public readonly double S;

    public ConicalUVMapper(double a_radius, double a_height)
    {
        S = a_radius / Math.Sqrt(a_radius * a_radius + a_height * a_height);
    }

    public override Vector2 Map(Vector3 a_uvw)
    {
        if (a_uvw.Y.AlmostEquals(1))
            return new Vector2(a_uvw.X, a_uvw.Z);
        else
        {
            double alpha = (Constants.PI - Math.Atan2(
               a_uvw.X - 0.5, 0.5 - a_uvw.Z)) * S;
            return new Vector2(0.5 - 0.5 * a_uvw.Y * Math.Cos(alpha), 
                                0.5 - 0.5 * a_uvw.Y * Math.Sin(alpha));
        }
    }
}

Brak komentarzy:

Prześlij komentarz