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:
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.
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
@antanthonline niesamowite! Proszę zrób! – Roliga
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