Wie funktionieren Multipass-Shader in OpenGL?

12

In Direct3D sind Multipass-Shader einfach zu verwenden, da Sie Übergänge innerhalb eines Programms buchstäblich definieren können. In OpenGL scheint dies etwas komplexer zu sein, da Sie einem Shader-Programm beliebig viele Vertex-, Geometrie- und Fragment-Shader zuweisen können.

Ein beliebtes Beispiel für einen Multipass-Shader ist ein Toon-Shader. Ein Durchgang bewirkt den eigentlichen Cel-Shading-Effekt und der andere erzeugt den Umriss. Wenn ich zwei Vertex-Shader "cel.vert" und "outline.vert" sowie zwei Fragment-Shader "cel.frag" und "outline.frag" habe (ähnlich wie in HLSL), wie kann ich das tun? kombinieren, um den vollständigen Toon-Shader zu erstellen?

Ich möchte nicht, dass du sagst, dass ein Geometrie-Shader dafür verwendet werden kann, weil ich nur die Theorie hinter Multipass-GLSL-Shadern kennen will;)

jmegaffin
quelle
"In Direct3D sind Multipass-Shader einfach zu verwenden, da Sie Übergänge innerhalb eines Programms buchstäblich definieren können." Nein, kannst du nicht. Sie können Übergänge in der FX-Datei definieren, dies ist jedoch nicht dasselbe wie bei einem HLSL-Shader.
Nicol Bolas

Antworten:

11

Es gibt keine "Theorie" hinter Multipass. Es gibt keine "Multipass-Shader". Multipass ist sehr einfach: Sie zeichnen das Objekt mit einem Programm. Dann zeichnen Sie das Objekt mit einem anderen Programm.

Sie können D3DX-Dateien wie FX-Dateien verwenden, um diese zusätzlichen Pässe zu verbergen. Aber sie arbeiten immer noch so. OpenGL hat einfach kein Versteck dafür.

Nicol Bolas
quelle
1

Rendern Sie das Objekt mit dem Zellenshader und rendern Sie es dann mit dem Gliederungsshader erneut.

Adam
quelle
1
Also sollte ich zwei verschiedene Shader-Programme machen? Warum kann ich dann einem Shader-Programm so viele Vertex- / Geometrie- / Fragment-Shader geben, wie ich möchte? Es muss eine Möglichkeit geben, dies mit einem Programm zu tun.
jmegaffin
1
Sie geben ihm nicht mehrere Vertex - Shader (usw.) Es gibt nur eine Hauptfunktion, sodass Sie nicht mehrere Einstiegspunkte haben. Es ist insofern einem C-Programm sehr ähnlich, als Sie mehrere Dateien haben können, die zu einem Programm verknüpft sind. Sie können diese Dateien für verschiedene Programme freigeben, damit Sie den Code nicht duplizieren müssen, aber jede Datei muss nicht unbedingt ein Programm für sich sein.
Chewy Gumball
1
Das ist der richtige Weg. Rendern Sie einmal mit einem Shader-Paar (Vertex + Fragment) und rendern Sie dann erneut mit einem anderen Paar (oder verwenden Sie denselben Vertex-Shader + einen anderen Fragment-Shader). Manchmal rendern Sie in einen Puffer und verwenden (noch) einen anderen Shader, um ihn in das Endergebnis zu „mischen“.
Valmond
1

In OpenGL 4.0 gibt es einheitliche Unterprogramme . Damit können Sie Funktionen definieren, die zur Laufzeit mit sehr geringem Aufwand ausgelagert werden können. Sie können also für jeden Durchgang eine Funktion ausführen. Außerdem 1 Funktion für jeden Shader-Typ.

Hier gibt es ein Tutorial .

Für ältere OpenGL-Versionen ist es am besten, mehrere verschiedene Shader zu haben und zwischen ihnen zu wechseln. Andernfalls können Sie Werte addieren, die Sie mit einer Uniform multiplizieren, die entweder 0.0 oder 1.0 ist, um sie ein- oder auszuschalten. Andernfalls ist es bedingt, wenn Anweisungen verwendet werden können, OpenGL jedoch bei jeder Ausführung / jedem Durchgang alle möglichen Ergebnisse ausführt. Stellen Sie also sicher, dass sie nicht zu schwer sind.

David C. Bishop
quelle
0

Es gibt einige Leute, die viel über GLSL wissen, also hoffe ich, dass sie den Rekord klarstellen, aber basierend auf dem, was ich gesehen habe, sollten Sie in Ihrem Fragment-Shader in der Lage sein, so etwas zu tun (Pseudocode, I) Überlassen Sie den tatsächlichen GLSL den Leuten, die ihn besser kennen.

out vec4 fragment_color
vec4 final_color
final_color = flat_color
If this fragment is in shadow
    final_color = shadow_color
If this fragment is an edge
    final_color = edge_color
If this fragment is a highlight
    final_color = highlight_color
fragment_color = final_color

Wenn Sie ifs auf diese Weise verwenden, erhalten Sie so etwas wie mehrere Durchgänge. Ist das sinnvoll?

Edit: Hoffentlich hilft diese Frage zu SO auch dabei , Missverständnisse auszuräumen.

Gemeinschaft
quelle
2
Es ist wichtig zu beachten, dass GLSL alle Anweisungen in den if-Zweigen bei jedem Durchlauf ausführt, auch wenn sie nicht aktiviert sind. Anscheinend ist es schneller, einfach alles zu berechnen und alle Farben zu addieren, die Werte jedoch entweder mit 1,0 oder 0,0 zu multiplizieren, um sie zu aktivieren oder zu deaktivieren.
David C. Bishop
1
@ DavidC.Bishop: Das stimmt nicht. Zumindest nicht garantiert wahr zu sein. Der Compiler entscheidet, ob ein tatsächlicher Zweig schneller ist oder ob der Ansatz "Alles berechnen und später aussortieren" schneller ist.
Nicol Bolas
1
Abgesehen davon ist das von @ DavidC.Bishop erwähnte Verhalten kaum spezifisch für Shader und die GPU. Moderne CPUs, die normale Programme ausführen, führen spekulativ mindestens einen der Zweige aus, wenn der getestete Wert noch nicht verfügbar ist. Wenn sich herausstellt, dass sie falsch sind, werden sie zurückgesetzt und erneut ausgeführt. Dies führt im Durchschnitt zu einer starken Beschleunigung.
ipeet