2013-08-03 7 views
17

Ich habe einige einfache Polygone (weniger als 20 Vertices), die flach auf einer einfachen xy-Ebene rendern, mit GL_TRIANGLES und einer flachen Farbe, einer 2D-Simulation.Einen Rahmen an einem 2D-Polygon mit einem Fragment-Shader zeichnen

Ich möchte diesen Polygonen einen Rahmen mit variabler Dicke und einer anderen Farbe hinzufügen. Ich habe etwas implementiert mit den gleichen Vertices und glLineWidth/GL_LINE_LOOP, das funktioniert, aber ist ein anderer Rendering-Durchlauf und wiederholt alle Vertex-Transformationen.

Ich denke, ich sollte dies in der Fragment-Shader mit gl_FragCoord und die Vertex-Daten und/oder Textur-Koordinaten tun können, aber ich bin mir nicht sicher, und meine naiven Versuche waren offensichtlich falsch.

Ich stelle mir etwas wie folgt vor.

uniform vec2 scale; // viewport h/w 
uniform float line_width; 
uniform vec4 fill_color; 
uniform vec4 border_color; 

varying vec4 vertex; // in world coords 

void main() 
{ 
    if (distance_from_edge(gl_FragCoord, vertex, scale) < line_width) 
    { 
     // we're close to the edge the polygon, so we're the border. 
     gl_FragColor = border_color; 
    } 
    else 
    { 
     gl_FragColor = fill_color; 
    } 
} 

Der Teil Ich versuche, die distance_from_edge Funktion, um herauszufinden, ist - wie das berechnet werden kann? Verwendet gl_FragCoord den falschen Ansatz - sollte ich eine Art Textur-Mapping verwenden?

Als Experiment habe ich versucht, den Vertex in Pixelraum mit der Skala zu konvertieren, und dann den Abstand zwischen diesem und gl_FragCoord in Pixeln zu berechnen, aber das gibt seltsame Ergebnisse, die ich nicht wirklich verstanden habe. Plus ich brauche den Abstand zum Rand, nicht der Scheitelpunkt, aber ich bin mir nicht sicher, wie man das bekommt.

Irgendwelche Ideen?

EDIT: basierend auf Nicol Antwort, meine Frage ist:

Ich habe als Kantenscheitelpunkte ein Dreieck mit 3 Eckenscheiteln Unter der Annahme, markiert, und ein Scheitelpunkt in der Mitte der Kante markiert als nicht (so mit 3 Dreiecken wiedergegeben in total), wie interpoliere ich dann im Fragment-Shader, um eine Grenze einer bestimmten Dicke zu zeichnen? Ich gehe davon aus, dass ich das Kanten-Flag an den Fragment-Shader sowie die gewünschte Linienstärke übergebe und eine Interpolationsberechnung durchführe, um den Abstand zwischen den Kanten- und Nicht-Kanten-Scheitelpunkten zu ermitteln und die Farbe als Grenze/Füllung entsprechend anzupassen ?

+1

'gl_FragCoord' wird massiv sein nicht hilfreich für Sie. Das sagt Ihnen nur, wo das Fragment auf dem Bildschirm ist; es sagt nichts darüber aus, wo das Fragment relativ zum Dreieck ist. Die Vertex-Position wird ebenfalls massiv nicht hilfreich sein. Sie benötigen die Hilfe des Vertex-Shaders/der vom Benutzer bereitgestellten Daten, die Werte auf Scheitelpunkte basierend darauf, ob es sich um einen Kantenscheitelpunkt handelt, setzen und den Wert interpolieren. Das bedeutet, dass Sie die Kante eines einzelnen Dreiecks nicht erkennen können. Sie müssten das Dreieck in mehrere Dreiecke aufteilen, so dass innere Ecken und Kantenscheitel unterschieden werden können. –

+0

Danke, ich vermutete, dass ich etwas verpasst habe. Also, wenn ich ein Dreieck mit 3 Eckpunkten habe, die als Eckpunkte markiert sind, und einem in der Mitte nicht (also mit 4 Dreiecken gerendert), wie interpoliere ich dann auf eine gegebene Linienbreite für den Rand? – bloodearnest

Antwort

23

Alles, was Sie brauchen, sind die baryzentrischen Koordinaten, da es sich um Dreiecke handelt. Weisen Sie jedem Eckpunkt des Dreiecks eine Identität zu und verwenden Sie dann die integrierte Interpolation zwischen den Vertex- und Fragmentstufen, um den relativen Abstand von jedem der Eckpunkte in Ihrem Fragment-Shader herauszufinden.

Sie können sich die baryzentrischen Koordinaten für jeden Eckpunkt als Abstand von der gegenüberliegenden Kante vorstellen. Im folgenden Diagramm ist die gegenüberliegende Kante des Eckpunkts P0 e1 und ihr Abstand ist durch h1 dargestellt; seine baryzentrische Koordinate ist <0.0, h1, 0.0>. GPUs können diesen Koordinatenraum intern verwenden, um Eckpunktattribute für Dreiecke zu interpolieren, wenn während der Rasterung Fragmente erzeugt werden, und es macht schnelle Arbeit der Gewichtung von Vertex-Eigenschaften basierend auf der Position innerhalb eines Dreiecks.

Diagram illustrating the calculation of distance from each edge

Im Folgenden zwei Tutorials, die erläutern, wie dies zu tun, in der Regel ist dies für die Darstellung eines Draht Overlay verwendet, so dass Sie die Suche mehr Glück für das haben könnten. Da dies effektiv eine Spezialisierung des Drahtgitter-Renderings ist (mit dem Zusatz, dass Sie Linien ausstoßen möchten, die nicht zu äußeren Polygonkanten gehören), müssen Sie Eckpunkte der Kanten identifizieren und zusätzliche Bearbeitungen durchführen.

Wenn zum Beispiel ein Scheitelpunkt nicht Teil einer äußeren Kante ist, dann möchten Sie ihm eine baryzentrische Koordinate von etwa < 1,100,0> und dem verbundenen Scheitelpunkt < 0,100,1> und der inneren Kante zuweisen ignoriert werden (unter der Annahme, dass es sich um eine Kante gegenüber dem Vertex handelt, der mit < 0,1,0> bezeichnet wird, wie im folgenden Diagramm zu sehen ist). Die Idee ist, dass Sie nie einen Punkt entlang dieser Kante wollen, um irgendwo zwischen 0.0 (oder was auch immer der Schwellenwert ist, den Sie für die Schattierung eines Fragments als Teil der Grenze verwenden) zu interpolieren, was es extrem von der Mitte des Dreiecks in der Richtung der gegenüberliegende Ecke wird dies lösen.

Diagram showing how to exclude interior edges

Ohne Geometrie Shaders (OpenGL ES freundlich):

Hier ist ein Link zu erklären, wie dies zu tun, wenn Sie in der Lage sind Ihre Vertex-Daten zu ändern, um die baryzentrischen Koordinaten zu halten. Es hat höhere Speicher- und Vorverarbeitungsanforderungen (insbesondere ist die gemeinsame Verwendung von Stützpunkten zwischen benachbarten Kanten möglicherweise nicht mehr möglich, da jedes Dreieck aus drei Stützpunkten bestehen muss, die jeweils eine andere baryzentrische Eingabe-Koordinate haben. Daher sind Geometrieshader a wünschenswerte Lösung). Es wird jedoch auf viel mehr OpenGL ES-Klassenhardware ausgeführt als allgemeinere Lösungen, die Geometrieshader erfordern.

http://codeflow.org/entries/2012/aug/02/easy-wireframe-display-with-barycentric-coordinates/

Mit Geometry Shaders ( Nicht OpenGL ES friendly):

Alternativ können Sie auch eine Geometrie-Shader verwenden die baryzentrischen Koordinaten für jedes Dreieck machen Zeit zu berechnen, wie in dieses Tutorial. Die Chancen stehen in OpenGL ES haben Sie keinen Zugriff auf Geometrie-Shader, so kann dies wahrscheinlich ignoriert werden.

http://strattonbrazil.blogspot.com/2011/09/single-pass-wireframe-rendering_10.html http://strattonbrazil.blogspot.com/2011/09/single-pass-wireframe-rendering_11.html

Die theoretische Basis für diese Lösung ist hier zu finden (mit freundlicher Genehmigung des Internet Archive Wayback Machine):

http://web.archive.org/web/ */http://cgg-journal.com/2008-2/06/index.html

+0

Danke, das hat mich auf dem richtigen Weg. Ich habe das Nicht-Geometrie-Shader-Wireframing implementiert und es funktioniert gut. Jetzt muss ich nur die zusätzlichen Non-Perimeter-Eckpunkte hinzufügen und entsprechend markieren. Ich hatte diesen Weg in meinen eigenen Experimenten schon teilweise mitgenommen, aber mit 1 Koord., Nicht 3. – bloodearnest

+3

Theoretische Basisverbindung ist unten, hier ein [alternativer Link (Webarchiv)] (https: //web.archive. org/web/20130607004602/http://cgg-journal.com/2008-2/06/index.html). Ein weiteres Papier von den gleichen Jungs: [Zwei Methoden für Antialiased Wireframe Drawing mit versteckter Linie Entfernung] (http: //orbit.dtu.dk/fedora/objects/orbit: 55459/datastreams/file_3735323/content) – fedab

+0

Leider führt diese Technik zu Artefakten, bei denen Grenzen sich Vertices annähern. Innere Dreieckskanten können Teile der Umrandung "abschneiden": http://imgur.com/Btl6h2Y. – John

Verwandte Themen