2012-04-24 13 views
8

Kilka dni temu zacząłem analizować efektywnie rysowanie krzywych Beziera, a natknąłem się na tę metodę opracowaną przez Charlesa Loopa i Jima Blinna, która wydawała się bardzo interesująca. Jakkolwiek, po wielu eksperymentach z ich algorytmem, po prostu nie mogę sprawić, żeby był w stanie wyrenderować sześcienne krzywe. Quadratics są w porządku, nie ma problemu.Krzywe Béziera, styl Loopa i Blinna

Jedyne zasobów znalazłem do tej pory są w następujący sposób:

GPU Gems 3 Chapter 25

Curvy Blues

Resolution Independent Curve Rendering using Programmable Graphics Hardware

Aby uzyskać testowania i działa szybko, robię to w XNA. Zasadniczo przekazuję współrzędne tekstury z moimi wierzchołkami do procesora graficznego, stosuję transformację perspektywy i używam formuły wspomnianej we wszystkich artykułach w module cieniującym pikseli, aby uzyskać końcowy wynik. Jakkolwiek, problem (jak sądzę) leży w sposobie obliczania współrzędnych tekstury. Sprawdź ten kod:

public void Update() 
{ 
    float a1 = Vector3.Dot(p1, Vector3.Cross(p4, p3)); 
    float a2 = Vector3.Dot(p2, Vector3.Cross(p1, p4)); 
    float a3 = Vector3.Dot(p3, Vector3.Cross(p2, p2)); 

    float d1 = a1 - 2 * a2 + 3 * a3; 
    float d2 = -a2 + 3 * a3; 
    float d3 = 3 * a3; 

    float discr = d1 * d1 * (3 * d2 * d2 - 4 * d1 * d3); 

    if (discr > 0) 
    { 
     Type = CurveTypes.Serpentine; 

     float ls = 3 * d2 - (float)Math.Sqrt(9 * d2 * d2 - 12 * d1 * d3); 
     float lt = 6 * d1; 
     float ms = 3 * d2 + (float)Math.Sqrt(9 * d2 * d2 - 12 * d1 * d3); 
     float mt = 6 * d1; 

     TexCoord1 = new Vector3(ls * ms, (float)Math.Pow(ls, 3), (float)Math.Pow(ms, 3)); 
     TexCoord2 = new Vector3((3 * ls * ms - ls * mt - lt * ms)/3, ls * ls * (ls - lt), ms * ms * (ms - mt)); 
     TexCoord3 = new Vector3((lt * (mt - 2 * ms) + ls * (3 * ms - 2 * mt))/3, (float)Math.Pow(lt - ls, 2) * ls, (float)Math.Pow(mt - ms, 2) * ms); 
     TexCoord4 = new Vector3((lt - ls) * (mt - ms), -(float)Math.Pow(lt - ls, 3), -(float)Math.Pow(mt - ms, 3)); 
    } 
    else if (discr == 0) 
    { 
     Type = CurveTypes.Cusp; 
    } 
    else if (discr < 0) 
    { 
     Type = CurveTypes.Loop; 
    } 
} 

Przepraszam za bałagan, to tylko część kodu testowego. p1 ... p4 są punktami kontrolnymi w przestrzeni świata, a TexCoord1 ... TexCoord4 są odpowiednimi współrzędnymi tekstury. Jest to replikacja tego, co zostało powiedziane w artykule Gems GPU.

Jest tu kilka problemów, najpierw przy obliczaniu a3 używamy p2 dla obu parametrów, co oczywiście zawsze daje w rezultacie wektor (0,0,0), a pobranie iloczynu kropek z tego i p3 będzie zawsze daj nam 0. To bezużyteczne, więc dlaczego mieliby o tym wspomnieć w artykule?

To oczywiście spowoduje, że discr będzie niepoprawny, a my nie będziemy nawet w stanie określić, jaki to jest krzywa.

Po obejrzeniu tego kodu przez chwilę zdecydowałem, że spróbuję zrobić dokładnie to, co zrobili w artykule w Loop i Blinn. Z tego otrzymuję coś takiego:

public void Update() 
{ 
    Matrix m1 = new Matrix(
     p4.X, p4.Y, 1, 0, 
     p3.X, p3.Y, 1, 0, 
     p2.X, p2.Y, 1, 0, 
     0, 0, 0, 1); 
    Matrix m2 = new Matrix(
     p4.X, p4.Y, 1, 0, 
     p3.X, p3.Y, 1, 0, 
     p1.X, p1.Y, 1, 0, 
     0, 0, 0, 1); 
    Matrix m3 = new Matrix(
     p4.X, p4.Y, 1, 0, 
     p2.X, p2.Y, 1, 0, 
     p1.X, p1.Y, 1, 0, 
     0, 0, 0, 1); 
    Matrix m4 = new Matrix(
     p3.X, p3.Y, 1, 0, 
     p2.X, p2.Y, 1, 0, 
     p1.X, p1.Y, 1, 0, 
     0, 0, 0, 1); 

    float det1 = m1.Determinant(); 
    float det2 = -m2.Determinant(); 
    float det3 = m3.Determinant(); 
    float det4 = -m4.Determinant(); 

    float tet1 = det1 * det3 - det2 * det2; 
    float tet2 = det2 * det3 - det1 * det4; 
    float tet3 = det2 * det4 - det3 * det3; 

    float discr = 4 * tet1 * tet3 - tet2 * tet2; 

    if (discr > 0) 
    { 
     Type = CurveTypes.Serpentine; 

     float ls = 2 * det2; 
     float lt = det3 + (float)((1/Math.Sqrt(3)) * Math.Sqrt(3 * det3 * det3 - 4 * det2 * det4)); 
     float ms = 2 * det2; 
     float mt = det3 - (float)((1/Math.Sqrt(3)) * Math.Sqrt(3 * det3 * det3 - 4 * det2 * det4)); 

     TexCoord1 = new Vector3(lt * mt, (float)Math.Pow(lt, 3), (float)Math.Pow(mt, 3)); 
     TexCoord2 = new Vector3(-ms * lt - ls * mt, -3 * ls * lt * lt, -3 * ms * mt * mt); 
     TexCoord3 = new Vector3(ls * ms, 3 * ls * ls * lt, 3 * ms * ms * mt); 
     TexCoord4 = new Vector3(0, -ls * ls * ls, -ms * ms * ms); 
    } 
    else if (discr == 0) 
    { 
     Type = CurveTypes.Cusp; 
    } 
    else if (discr < 0) 
    { 
     Type = CurveTypes.Loop; 
    } 
} 

Zgadnij co, to też nie działa. Jak dotąd dyskrecja wydaje się teraz co najmniej bardziej poprawna. Przynajmniej ma prawidłowy znak i wynosi zero, gdy punkty kontrolne są ułożone tak, aby tworzyły wierzchołek. Nadal mam ten sam efekt wizualny, z tym że krzywa znika losowo przez pewien czas (formuła cieniowania pikseli jest zawsze większa od zera) i powraca po przesunięciu punktu kontrolnego z powrotem do bardziej kwadratowego kształtu. Oto kod cieniowania pikseli, a tak przy okazji:

PixelToFrame PixelShader(VertexToPixel PSIn) 
{ 
    PixelToFrame Output = (PixelToFrame)0; 

    if(pow(PSIn.TexCoords.x, 3) - PSIn.TexCoords.y * PSIn.TexCoords.z > 0) 
    { 
    Output.Color = float4(0,0,0,0.1); 
    } 
    else 
    { 
    Output.Color = float4(0,1,0,1); 
    } 

    return Output; 
} 

Chodzi o wszystkie przydatne informacje, które mogę teraz przeczytać. Czy ktoś ma pojęcie, co się dzieje? Ponieważ mi ich brakuje.

+0

Sam zacząłem wdrażać tę metodę i odświeżam, gdy coś będzie działać. Chciałem tylko, żebyś wiedział, że to pytanie nie zostało porzucone :) – Ani

+0

@antanthonline niesamowite! Proszę zrób! – Roliga

+0

Mam również problemy z tym. Czy to działa? Możesz odpowiedzieć na moje pytanie? http://stackoverflow.com/questions/15519142/resolution-independent-cubic-bezier-drawing-on-gpu-blinn-loop – scippie

Odpowiedz

7

Patrzyłem na papier i twój kod, i to szwy, że brakuje mnożenia do macierzy M3.

Twoje współrzędne p1, p2, p3 i p4 powinny być umieszczone w macierzy i pomnożone przez macierz M3, przed użyciem jej do obliczenia wyznaczników. np.

Matrix M3 = Matrix(
    1, 0, 0, 0, 
    -3, 3, 0, 0, 
    3, -6, 3, 0, 
    -1, 3, -3, 1); 
Matrix B = Matrix(
    p1.X, p1.Y, 0, 1, 
    p2.X, p2.Y, 0, 1, 
    p3.X, p3.Y, 0, 1, 
    p4.X, p4.Y, 0, 1); 
Matrix C = M3*B; 

Następnie należy użyć każdego wiersza macierzy C jako współrzędnych macierzy od m1 do m4 w kodzie. Gdzie pierwsza i druga wartość wiersza są współrzędnymi x, y, a ostatnia jest współrzędną w.

Wreszcie macierz współrzędnych tekstury musi zostać zmultiplikowana przez odwrotność M3 np.

Matrix invM3 = Matrix(
    1, 0, 0, 0, 
    1, 0.3333333, 0, 0, 
    1, 0.6666667, 0.333333, 0, 
    1, 1, 1, 1); 
Matrix F = Matrix(
    TexCoord1, 
    TexCoord2, 
    TexCoord3, 
    TexCoord4); 
Matrix result = invM3*F; 

Każdy wiersz wynikowej macierzy odpowiada współrzędnym tekstury potrzebnej dla modułu cieniującego.

Nie zaimplementowałam go jeszcze, więc nie mogę zagwarantować, że rozwiąże on Twój problem. Po prostu to, co zauważyłem, brakuje w twoim wdrożeniu po przeczytaniu artykułu.

Mam nadzieję, że to pomoże, jeśli się mylę, proszę powiedz mi, bo wkrótce to wypróbuję.

+0

Awesome man! Nie mogę uwierzyć, że źle to zepsułem: D Problem jednak jest, det1 zawsze wydaje się być 0, co z pewnością nie jest właściwe. Mam zamiar zrobić jutro więcej testów i sprawdzić, czy uda mi się znaleźć problem. Powiedz mi, jeśli masz jakieś pomysły. – Roliga

+1

W artykule w dziale Integral Cubics (4.4) w pierwszym akapicie mówi się, że dla integralnych modułów będzie miał pierwszą wyznacznik równą zero. Który redukuje równanie kodu cieniującego do k^3-lm – rdsgalvao

+0

No właśnie. Powodem, dla którego myślałem, że coś jest nie tak, było to, że myślałem tylko, że krzywe, które faktycznie mają pętlę, mają używać równania pętli, ale tak się nie stało, więc po wdrożeniu równania pętli wszystko działało. Przejdźmy teraz do struktury tej rzeczy, aby mogła być faktycznie użyta: D Dodam kolejny komentarz, jeśli napotkam więcej problemów. – Roliga

Powiązane problemy