OpenGL: Wo soll ich Shader platzieren?

17

Ich versuche, OpenGL ES 2.0 zu lernen, und frage mich, welche Methode zum "Verwalten" von Shadern am häufigsten angewendet wird.
Ich stelle diese Frage, weil ich in den Beispielen, die ich gefunden habe (wie in der API-Demo, die mit dem android sdk geliefert wurde), normalerweise alles in der GLRenderer-Klasse sehe und die Dinge lieber trennen möchte, damit ich sie haben kann. Zum Beispiel ein GLImage-Objekt, das ich wiederverwenden kann, wenn ich ein texturiertes Quad zeichnen möchte (ich konzentriere mich momentan nur auf 2D), so wie ich es in meinem OpenGL ES 1.0-Code getan habe. In fast jedem Beispiel, das ich gefunden habe, werden Shader nur als Klassenattribute definiert. Beispielsweise:

public class Square {

public final String vertexShader =
        "uniform mat4 uMVPMatrix;\n" +
        "attribute vec4 aPosition;\n" +
        "attribute vec4 aColor;\n" +
        "varying vec4 vColor;\n" +
        "void main() {\n" +
        "  gl_Position = uMVPMatrix * aPosition;\n" +
        "  vColor = aColor;\n" +
        "}\n";

public final String fragmentShader =
        "precision mediump float;\n" +
        "varying vec4 vColor;\n" +
        "void main() {\n" +
        "  gl_FragColor = vColor;\n" +
        "}\n";
// ...
}

Ich entschuldige mich im Voraus, wenn einige dieser Fragen dumm sind, aber ich habe noch nie mit Shadern gearbeitet.

1) Ist der obige Code der gebräuchliche Weg, um Shader zu definieren (öffentliche Eigenschaften der letzten Klasse)?
2) Sollte ich eine separate Shader-Klasse haben?
3) Wenn Shader außerhalb der Klasse definiert sind, die sie verwendet, wie würde ich die Namen ihrer Attribute kennen (z. B. "aColor" im folgenden Code), damit ich sie binden kann?

colorHandle = GLES20.glGetAttribLocation(program, "aColor");
Miviclin
quelle

Antworten:

16

Ich habe diese Art der Definition von Shadern (in einer Zeichenfolge) immer abgelehnt. Ich bevorzuge es, meins in einer Textdatei zu machen und sie beim Laden einzulesen. Das Definieren in einer Zeichenfolge ist für das Debuggen ärgerlich und sieht für mich nur chaotisch aus. Es ist einfach viel einfacher, es einzutippen und zu sehen, wie es formatiert sein sollte, anstatt in einer Zeichenfolge.

Ich habe auch eine separate Klasse, die gemeinsame Shader-Funktionen hat, wie das Einlesen von Shadern und das Drucken von Protokollinformationen für das Shader-Debugging.

Wo immer die Shader definiert sind, können Sie wie in Ihrem Beispiel auf die Namen zugreifen. Die Verwendung des Zeichenfolgenliteral ist für Shader akzeptabel, da sich diese Werte wahrscheinlich nicht ändern, sobald Sie sie implementiert haben.

Am Ende liegt es jedoch an Ihnen. So wie Sie es gerade machen, funktioniert es hervorragend, wenn es Ihnen keine Probleme bereitet.

MichaelHouse
quelle
2
Wenn Sie Ihre Shader in separaten Dateien haben, können Sie auch Ihre handlichen Shader-Editoren verwenden und die Syntax vollständig hervorheben und sogar eine Vorschau anzeigen, bevor Sie sie in Ihrem Programm testen, sofern dies von Ihnen unterstützt wird.
Raceimaztion
6
Der einzige wirkliche Grund, warum Shader in einer Zeichenfolge enthalten sind, sind schnelle Tests und Lernprogramme. Bei der Dateiverwaltung wird ein Großteil des "unnötigen Codes" hinzugefügt.
Jari Komppa
2
Sie können auch Hot-Reload für Shader-Dateien implementieren, um Änderungen zu debuggen, ohne das Spiel neu zu starten. Produktivität ++
Liosan
Ich mag die Idee, sie als Datei zu lesen und eine separate Shader-Klasse zu haben. Es verwirrte mich, sie immer in einem String zu sehen, da ich nicht wusste, ob sie normalerweise so definiert sind oder nur zu Lernzwecken. Vielen Dank!
miviclin
8

Das Shader-Management (und damit auch das Material-Management) ist ein ziemlich kniffliges Problem, wenn Ihr Grafiksystem komplexer wird und Sie bemerken, dass hartes Codieren bei jedem Shader zu massiven Code-Duplikationen führen würde. Hier sind einige alternative Lösungsmöglichkeiten:

  • Kleine Beispiele, in denen es nur ein paar Shader gibt, tendieren dazu, sie als Zeichenfolgen fest zu codieren, um die Dateibehandlung zu vermeiden, wie Jari Komppa kommentierte
  • Besser ist es, separate Dateien zu verwenden, in denen Sie die Syntax hervorheben und korrekt formatieren können. Sie können auch ein einfaches Debug-System programmieren, das Änderungen in diesen Dateien überwacht und den modifizierten Shader während des Spielbetriebs sofort anwendet.
  • Wenn die Materialanzahl zunimmt, wird ein Shader-Generator fast zur Notwendigkeit. Grundsätzlich definieren Sie allgemeine Snippets wie "Fragment Shader Normal Mapping Snippet" und stellen Ihre kompletten Shader zusammen, indem Sie die gewünschten Komponenten einbeziehen.
    • Sie könnten hartcodierte String-Schnipsel verwenden, aber das wird sehr schnell chaotisch. Wiederum sind Dateien eine gute Idee.
    • Sie können entweder Code-Bits in separaten Dateien (oder in denselben) haben oder alternativ Ubershader / Supershader verwenden, wobei Sie den Präprozessor (oder einheitliche Flags) verwenden, um Dinge zu aktivieren / deaktivieren.

Für die Attribut- und Uniformnamen und dergleichen verwenden Sie einfach eine einheitliche Benennung für alle Shader.

Tapio
quelle
Ich werde meine Shader definitiv in eine separate Datei verschieben. Vielen Dank!
Miviclin