2010-02-17 9 views
13

Wie ändere ich die Linienstärke beim Zeichnen von Linien mit Direct3D?Direct3D Linienstärke

This post sagt, dass die Linienbreite nicht unterstützt wird und geht auf eine Abhilfe zu schaffen. Andere Optionen?

Während wir zu diesem Thema sind, tun Shadern erlauben eine Linien mit Strichmuster zu zeichnen?

Antwort

14

Sie können einen Geometrieshader verwenden, der als Eingabe ein Segment Ihrer Linie verwendet und ein Quad (ein Dreieck aus zwei Dreiecken) ausgibt, so dass die Breite des Quads im Bildschirm konstant ist und dem Wunsch entspricht dicke der Linie. Es funktioniert perfekt (für die Implementierung in einer CAD 3D Engine).

Wenn Geometrie-Shader keine Option ist, könnte eine Abhilfe, einen Vertex-Shader zu verwenden, aber es wird noch einige Nacharbeiten Ihrer VB benötigen. Denken Sie daran, dass der VS dann Kenntnis über das Liniensegment in seinem Ganzen haben muss, so dass Sie am Ende p und p + 1 für jedes Ihrer VB Elemente plus die Kosten der Duplizierung von Indizes/Scheitelpunkten speichern (abhängig von der verwendeten Topologie und wenn Sie Ihre Linie als indizierte Primitive rendern oder nicht).

Wenn die Leistung kein Problem ist, ist die Erweiterung der CPU vielleicht der richtige Weg.

EDIT:

Über Strichmuster: Sie haben eine Geometrie-Shader zu, um glLineStipple Verhalten emulieren können. Wenn Sie eine GL_LINES Topologie haben, also isolierte Linien, wird das Muster an jedem neuen Liniensegment neu gestartet. Sie müssen also nur im Geometrieshader den horizontalen Start (oder vertikalen Start, abhängig von der Ausrichtung) Ihres Liniensegments berechnen und diese zusätzlichen Informationen an den Pixelshader übergeben. Der Pixel-Shader wird dann verantwortlich das Verwerfen Fragments nach dem Faktor und Muster Werte (DirectX 10/11 Integer und Bitwise Anweisungen erleichtern).

Wieder funktioniert dies gut, und man kann es mit emulierten Breite Linien (mit der ersten Technik, die oben erwähnt) kombinieren.

Jetzt, wenn Sie GL_LINE_STRIP Topologie haben, startet das Muster bei jedem neuen Grundelement neu (also für jeden neuen Zeichnungsaufruf). Die Situation wird etwas komplizierter, da Sie die Anzahl der Pixel kennen müssen, die zuvor gerendert wurden, und dies für jedes Liniensegment.

Sie können dies erreichen, indem Sie Ihren Linienstreifen in einem temporären VB mit DirectX 10 stream-out-Funktionalität rendern (jedes Element dieses VB entspricht der Bildschirmlänge jedes Segments). Dann müssen Sie eine Parallel-Präfix-Summe (aka Scan) dieser VB (zum Akkumulieren jeder Liniensegmentlängenwerte) machen.

Schließlich Sie Ihre Linie Streifen wie für GL_LINES machen, aber diese zusätzliche Scan VB Informationen im PS verwenden.

13

Die Liniendicke wird nicht nur von Direct3D unterstützt, noch wird sie von einer derzeit vorhandenen GPU unterstützt. Es gibt keine GPU, von der ich weiß, dass sie sogar überhaupt richtige Linien zeichnen kann (sie alle fälschen Linien mit degenerierten Polygonen - das heißt, die zweiten und dritten Ecken sind an der gleichen Position, so dass das Dreieck im Wesentlichen zu einer einzigen Linie kollabiert)).

Während es möglich ist, eine Linienbreite in OpenGL festzulegen, erstellt der OpenGL - Treiber extrudierte Quads (die nicht von der aktuellen GPU unterstützt werden und mit zwei Dreiecken für jedes Quad emuliert werden), um die "Linien" auf die Bildschirm.

Also gibt es wahrscheinlich keine Möglichkeit, extrudierte Quads für diesen Zweck zu erstellen. Es gibt mehrere Möglichkeiten, dies zu erreichen, wie Stringer Bell in seiner Antwort erklärte.

Der einfachste Weg, der für mich funktioniert, besteht darin, einen Vertex-Puffer zu erstellen, der jeden Eckpunkt zweimal enthält, wobei die Normalen "rechts" oder "links" zeigen, je nachdem, ob sie die rechte oder linke Kante der Linie sind. Dann kann ein sehr einfacher Vertex-Shader die Extrusion durchführen (indem er die Eckpunktposition um ein Vielfaches seines normalen Vektors ändert). Auf diese Weise können Sie schnell die Linienbreite ändern, ohne Ihre Geometrie auf der CPU neu berechnen zu müssen. Dies kann nützlich sein, wenn Sie die Linienbreite abhängig von der Größe oder Entfernung des Objekts anpassen möchten.

+0

Dies ist eine interessante Lösung. Haben Sie erreicht, dass Sie die gleichen Ergebnisse erhalten wie mit einem glLineWidth-Aufruf (pro Pixel)? – Stringer

+0

Ich habe es nicht per-Pixel überprüft, aber für untexturierte, Antialias-Linien sollten die Ergebnisse ausreichend ähnlich sein. Die Verwendung einer Textur (zum Beispiel, um OpenGLs Muster zu emulieren) wird Sie mit Sicherheit in eine Welt des Schmerzes versetzen, mit nichtlinearer Interpolation von Texturkoordinaten (welche NVidia Quadros überraschend schlecht handhaben, sogar im Vergleich zum meist identischen Geforce Teil). Der Shader muss eine Menge zusätzlicher Arbeit leisten, um die Texcoords richtig zu machen. – Pepor

+0

Sie können das selbst ausprobieren, verwenden Sie einfach eine Art "Zeilenlänge in Bildschirmpixeln" für Texturkoordinaten und zeichnen Sie die Linie (n) mit einer Textur, die ein Punktierungsmuster hat. Sie werden überrascht sein, wie ungleich das Muster ist. Zumindest war ich es. ;-) – Pepor

0

In Bezug auf Ihre Strichmuster (Punktierung) Frage, Ihre beste Wette ist wahrscheinlich die Linie als dünne Quad zu machen und eine Textur, um es in den Pixel-Shader anwenden, wo die Textur Ihre Strichmuster enthält. Sie müssen den Sampler-Adressmodus so einstellen, dass er wie folgt umrahmt:

SamplerState wrapTriLinear { 
    AddressU = WRAP; 
    AddressV = WRAP; 
}