2012-04-24 5 views
8

Vor ein paar Tagen fing ich an, effizient Bezier Kurven zu zeichnen, und ich stieß auf eine von Charles Loop und Jim Blinn entwickelte Methode, die sehr interessant schien. Wie auch immer, nachdem ich viel mit ihrem Algorithmus experimentiert habe, kann ich einfach nicht sehen, dass sie in der Lage ist, die kubischen Kurven zu rendern. Quadratics sind in Ordnung, kein Problem.Bézier Kurven, Loop und Blinn Stil

Die einzigen Ressourcen, die ich bisher gefunden haben, sind wie folgt:

GPU Gems 3 Chapter 25

Curvy Blues

Resolution Independent Curve Rendering using Programmable Graphics Hardware

die Prüfung bis zu bekommen und schnell läuft, mache ich dies in XNA. Im Grunde gebe ich Texturkoordinaten mit meinen Scheitelpunkten an die GPU weiter, wende eine perspektivische Transformation an und verwende die in allen Artikeln eines Pixel-Shaders genannte Formel, um das Endergebnis zu rendern. Wie auch immer, das Problem (denke ich) liegt darin, wie ich die Texturkoordinaten berechne. Überprüfen Sie diesen Code aus:

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; 
    } 
} 

Entschuldigen Sie die Unordnung, es ist nur ein paar Test-Code. p1 ... p4 sind die Kontrollpunkte im Weltraum und TexCoord1 ... TexCoord4 sind die entsprechenden Texturkoordinaten. Dies ist eine Replikation von dem, was in dem GPU Gems Artikel gesagt wird.

Es gibt ein paar Probleme hier, zuerst bei der Berechnung von a3, wir verwenden p2 für beide Parameter, was natürlich immer zu einem (0,0,0) Vektor führt und das Skalarprodukt von diesem und p3 immer nimmt gib uns 0. Das ist ziemlich nutzlos, also warum sollten sie das in dem Artikel erwähnen?

Dies wird natürlich discr falsch machen, und wir werden nicht einmal in der Lage sein zu bestimmen, welche Art von Kurve es ist.

Nachdem ich eine Weile mit dem Code herumgespielt hatte, beschloss ich, genau das zu tun, was sie in der Loop- und Blinn-Arbeit getan hatten. Von diesem bekomme ich etwas in der Art:

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; 
    } 
} 

Rate mal, das hat auch nicht funktioniert. Wie dem auch sei, discr scheint jetzt zumindest ein wenig korrekter zu sein. Zumindest hat es das richtige Vorzeichen, und es ist Null, wenn die Kontrollpunkte so angeordnet sind, dass sie eine Spitze bilden. Ich erhalte immer noch das gleiche visuelle Ergebnis, nur dass die Kurve für eine Weile zufällig verschwindet (die Pixel-Shader-Formel ist immer größer als Null) und kehrt zurück, nachdem ich den Kontrollpunkt zurück zu einer eher quadratischen Form gebracht habe. Hier ist der Pixel-Shader-Code übrigens:

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; 
} 

Das ist über alle nützlichen Informationen, die ich gerade denken kann. Hat jemand eine Idee was los ist? Weil ich ihnen auslaufe.

+0

Ich fing an, diese Methode selbst zu implementieren und werde zurück schreiben, wenn ich etwas arbeite. Wollte nur, dass Sie wissen, dass diese Frage nicht aufgegeben wird :) – Ani

+0

@ananthonline genial! Bitte! – Roliga

+0

Ich habe auch Probleme damit. Hast du es zur Arbeit gebracht? Kannst du meine Frage beantworten? http://stackoverflow.com/questions/15519142/resolution-independent-cubic-bezier-drawing-on-gpu-blinn-loop – scippie

Antwort

7

Ich schaute auf das Papier und Ihren Code, und es scheint, dass Sie die Multiplikation zur M3-Matrix vermissen.

Ihre p1-, p2-, p3- und p4-Koordinaten sollten in einer Matrix platziert und mit der M3-Matrix multipliziert werden, bevor sie zur Berechnung der Determinanten verwendet wird. z.

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; 

Dann nutzen Sie jede Zeile der C-Matrix als die Koordinaten für die m1 Matrizen in Ihrem Code m4. Wo der erste und der zweite Wert der Reihe die x, y Koordinaten sind und der letzte die w Koordinate ist.

Schließlich muss die Matrix der Texturkoordinaten um das Inverse von M3 multipliziert werden.

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; 

Jede Zeile der resultierenden Matrix entspricht den Texturkoordinaten, die für den Shader benötigt werden.

Ich habe es selbst noch nicht implementiert, kann also nicht garantieren, dass es Ihr Problem lösen wird. Es ist einfach das, was ich nach der Lektüre der Arbeit bei Ihrer Implementierung übersehen habe.

Ich hoffe, dies hilft, wenn ich falsch liege, bitte sagen Sie mir, weil ich das bald ausprobieren werde.

+0

Awesome Mann! Ich kann nicht glauben, dass ich es so schlecht vermasselt habe: D Das Problem ist, dass det1 immer 0 ist, was definitiv nicht richtig ist. Ich werde morgen noch ein paar Tests machen und schauen, ob ich das Problem finden kann. Sag mir, wenn du Ideen hast. – Roliga

+1

In der Abhandlung im Abschnitt "Integral Cubics" (4.4) im ersten Abschnitt heißt es, dass die erste Determinante für integrale Kubik gleich Null ist. Was reduziert die Shader-Code-Gleichung zu k^3-lm – rdsgalvao

+0

Oh richtig. Der Grund, warum ich dachte, dass etwas nicht stimmt, war, dass ich dachte, dass Kurven, die eigentlich eine Schleife haben, die Schleifengleichung verwenden sollen, aber das schien nicht der Fall zu sein, also funktionierte alles nach der Implementierung der Schleifengleichung. Jetzt über die Strukturierung dieser Sache, damit sie tatsächlich verwendet werden kann: D Ich werde einen weiteren Kommentar fallen lassen, wenn ich auf weitere Probleme stoße. – Roliga