OpenGL - reicht ein Shader?



  • Hallo liebe Leser,

    ich stehe gerade davor für meine Engine eine nicht unbedeutende Entscheidung zu fällen.

    Ich bin am überlegen, ob es sinnvoller wäre, viele spezielle, einzelne Shader zu haben, oder einen, der für alle Modelle benutzt werden kann.

    Unter einem "speziellen" Shader stelle ich mir beispielsweise je einen Shader vor, der
    - eine Textur
    - eine Farbe
    - eine Farbpalette
    usw. (oder auch Kombinationen davon)
    voraussetzt, aber eben auch nicht mehr verarbeiten kann.

    Z.B. habe ich einen Würfel, der aus unterschiedlichen Materialien besteht, so sind zwei Flächen einfarbig, zwei weitere bestehen aus Farben einer Farbpalette und die letzten beiden sind mit einer Textur gefüllt, die von einer Farbe eingefärbt wird.
    Also benötige ich für jeweils zwei Flächen einen Shader.
    (In diesem Fall
    - Farbe
    - Farbpalette
    - Textur, Farbe
    )

    Natürlich ließe sich das in der Theorie auch über einen Universalshader lösen, der (beispielsweise durch ein Weiteres Argument "mode") erfährt, welche Argumente er für die unterschiedlichen Flächen benötigt.

    Jetzt fragte ich mich, welche Vorteile und Nachteile es dabei gibt (ich schließe hierbei nicht aus, dass es für sehr spezifische Flächen auch noch andere Shader gibt).

    Ich gehe davon aus, dass man durch die Nutzung eines Universalshaders zum Einen an Speicherplatz, als auch an einigen "glUseProgram" Aufrufen spart und sich desweiteren erspart, die Modelle, bzw. die einzelnen Flächen nach dem Shader zu ordnen und in Reihenfolge zu zeichnen.
    Im Gegenzug denke ich, dass man durch die Größe des Shaders und die nötigen "if"-Bedingungen (um herauszufinden welche Berechnung verwendet werden muss) ein wenig an Performance in der Grafikkarte verliert.

    Auch fragte ich mich, ob es überhaupt möglich ist, den Shader auszuführen, ohne alle Argumente auszufüllen.

    Vielleicht bin ich ja nicht allein mit dem Gedanken, oder es fällt sonst Jemandem etwas dazu ein.

    In jedem Fall,
    Vielen Dank und ab dafür.
    --Zuzu_Typ--



  • Hi!

    So wie ich das sehe, macht es überhaupt keinen Sinn, für sämtliche Modelle einen einzelnen Shader zu wählen, weil (eingehend auf Deine Fragestellungen):

    - das Z-Sorting spätestens bei komplexeren Szenen mit halbdurchsichtigen Flächen irgendwie im Vorfeld passieren muss,
    - der tatsächliche Speicher, der von übersetzten Shadern auf modernen Grafikkarten belegt wird (>= 2GB), absolut marginal ist im Vergleich zu Vertex- und Texturdaten
    - so ein extremes Branching, wie du es für die ganzen Sonderfälle bräuchtest, für eine SIMD-ähnliche Architektur wie die GPU absolut ausbremst,
    - auch aus Coding-Style-Sicht so ein monolithischer Shader absolut nertötend wäre (denk an die armen Programmierer, die deine Engine benutzen wollen),
    - es imho absolut unmöglich ist, alles abzudecken, was man mit Shadern so anstellen kann.

    Viel sinnvoller ist es, wenn du dir Gedanken darüber machst, wie du die einzelnen Shader sinnvoll aggregieren kannst, um so viele glUseProgram()-Aufrufe wie möglich zu sparen. Als Idee dafür bieten sich z.B. Gruppen-Queues an, mit denen du schonmal einiges zusammenfassen kannst. Auch Screen-Space-Rendering-Methoden helfen dir hier; der Gedanke hier ist grob erklärt, dass man die einzelnen Informationen über ein Fragment in verschiedene FrameBuffer rendert und dann später in irgendeiner Form zusammenfasst. Wenn du hier die Tiefe des Fragments in einem separaten Buffer hast, kannst du später eine Menge damit anstellen, ohne groß auf die Reihenfolge der draw calls zu achten. Natürlich gibt's hier Grenzen und unterschiedlich komplizierte Verfahren, diese zu überwinden, aber das ist auf jeden Fall ein moderner Weg, sowas effizient zu lösen.

    Zu deiner anderen Frage: Soweit ich weiß, ist es ganz stark davon abhängig, welcher Treiber/welche GL Version zum Einsatz kommt, was das Binden von Uniforms angeht. Auf meinem Android wirft mir das regelmäßig fehler, der Rechner ist da gnädiger. Am besten fährst du, wenn du immer jede Uniform-Variable bindest, sicher ist sicher.

    Ich hoffe, das hat dir ein wenig geholfen 🙂


  • Mod

    wenn du dir diese frage stellst, dann hast du vermutlich nicht soviel erfahrung mit shadern. Aus diesem grund wuerde es dir recht unmoeglich sein zu wissen wie ein generischer shader aussehen sollte. (du bist ja noch nicht in alle faelle gelaufen die auftreten koennen, deswegen kannst du nicht dafuer konzeptionieren).

    Deswegen, der einfachheit wegen, mach wenige shader, die moeglichst viel machen, aber brich dir nichts ab nur um dabei zu bleiben, implementiere einen neuen wann auch immer es dir als "einfacher" erscheint.

    mach dir auch keine sonderlichen gedanken ueber andere umstaende wie sortieren oder performance, denn das gehst du am besten an wenn du die probleme hast. vorher kannst du nichtmal realistisch testen, ob deine optimierungsarbeit ueberhaupt etwas bringt.

    kurz: mach das, was oekonomisch fuer deine arbeitszeit ist, das ist das wichtigste. Ignorieren probleme nicht, aber loese sich auch nicht bevor sie ueberhaupt da sind.



  • aaaaaaaaargh schrieb:

    denk an die armen Programmierer, die deine Engine benutzen wollen

    - Ich bin zum Glück der einzige Programmierer, der mit meiner Engine arbeiten muss 😉 .

    aaaaaaaaargh schrieb:

    Viel sinnvoller ist es, wenn du dir Gedanken darüber machst, wie du die einzelnen Shader sinnvoll aggregieren kannst, um so viele glUseProgram()-Aufrufe wie möglich zu sparen. Als Idee dafür bieten sich z.B. Gruppen-Queues an, mit denen du schonmal einiges zusammenfassen kannst. Auch Screen-Space-Rendering-Methoden helfen dir hier; der Gedanke hier ist grob erklärt, dass man die einzelnen Informationen über ein Fragment in verschiedene FrameBuffer rendert und dann später in irgendeiner Form zusammenfasst. Wenn du hier die Tiefe des Fragments in einem separaten Buffer hast, kannst du später eine Menge damit anstellen, ohne groß auf die Reihenfolge der draw calls zu achten.

    - Wäre das nicht leicht verschwenderisch? Die "richtige Reihenfolge" zu bestimmen, ist (mal von transparenten Flächen abgesehen) nicht sonderlich aufwendig, da nur Flächen, die den selben Shader und das gleiche Material verwenden zusammen gezeichnet werden müssen.

    Ich war übrigens schon vorher davon ausgegangen, dass es irgendeinen guten Grund dafür gibt, keinen globalen Shader zu machen, da das ja von Niemandem so getan wird.
    Deshalb möchte ich mich für die klare(n) Antwort(en) bedanken.
    Also - Dankeschön.


Log in to reply