GLSL-Standard-Shader

7

Ich habe meinen Motor mit einem Fehlerprüfcode scharfgeschaltet. Ich werde versuchen, diese Situation nach besten Kräften zu beschreiben. Immer wenn ich einen Shader lade und ein Fehler auftritt (Datei existiert nicht, Kompilierungsfehler, alles, was schief gehen könnte), lösche ich den Shader und setze den Zeiger auf 0 (ich kann mir keinen anderen Weg vorstellen, um sicherzugehen, dass ich es nicht tue versuchen, versehentlich Müll anzuhängen oder zu löschen). Ich möchte das Programm nicht stoppen, wenn dies passiert, weil jemand versehentlich eine einzelne Datei im Spielordner löschen oder ändern könnte und deshalb nicht das ganze Spiel unbrauchbar machen möchte.

Nun ist die Sache, gibt es ein Standardverhalten in der Situation, in der ich Shader an ein Programm anhänge, dessen Zeiger gleich 0 ist?

Wenn ich in meinem Programm einen Vertex-Shader gleich 0 und einen Fragment-Shader gleich 0 anhänge, verwendet OpenGL das Standardprogramm (zeigt alles weiß an):

Geben Sie hier die Bildbeschreibung ein

Dies ist ein gedrehter Würfel, dessen Abmessungen sich bereits im Clipbereich befinden und ohne perspektivische Projektion oder Tiefenprüfung gerendert werden. Es wird aus einer OBJ-Datei geladen.

Wenn ich nun einen richtigen Fragment-Shader anhänge, der Vertex-Shader jedoch 0 ist, erhalte ich Folgendes:

Geben Sie hier die Bildbeschreibung ein

Was ich erwarten würde, alles, was der Fragment-Shader tut, ist die Ausgabe eines vec4 (1.0, 0.3, 0.7, 1.0). Es gibt keinen Vertex-Shader, also verwendet OpenGL den Standard (ist dies eine korrekte Annahme?).

Wenn ich jetzt jedoch einen korrekten Vertex-Shader anhänge, aber einen Fragment-Shader gleich 0 anhänge (oder ich füge überhaupt keinen Fragment-Shader an), bekomme ich dieses Durcheinander:

Geben Sie hier die Bildbeschreibung ein

Was Sie nicht sehen können, ist, dass dieses Modell ständig in verschiedenen Farben lackiert wird. Auf meinem Bildschirm sieht es also aus wie ein Säuretrip mit schwarzen horizontalen Linien.

Ich bin ein bisschen verwirrt, weil ich erwarten würde, dass der Würfel nur weiß ist. Ich möchte darauf hinweisen, dass mein Shader-Code Bare Bones ist. Ich tausche keine Variablen zwischen Vertex- und Fragment-Shadern aus. Wenn ich überprüfe, ob der Zeiger nicht gleich 0 ist, ändert sich nichts. Das verknüpfte Programm verhält sich so, als hätte ich ihm einen Zeiger gleich 0 gegeben. Das Seltsamste für mich ist, dass es immer schön schattiert ist, es ist nicht nur Müll.

BEARBEITEN: Vielleicht erwartet der Standardfragment-Shader einen gl_-Wert von dem Scheitelpunkt, den ich festlegen möchte? Das würde irgendwie erklären, warum die Oberfläche schön schattiert ist, der Fragment-Shader erwartet ein Glätten, aber dieser Wert ist nur Müll, weil ich ihn nicht gesetzt habe.

Ist dies nun ein "undefiniertes Verhalten" oder soll es so funktionieren?

Dreta
quelle
Ich möchte feststellen, dass der Schutz vor "jemand könnte eine Datei löschen" nicht besonders produktiv ist. Es ist genauso wahrscheinlich, dass sie Ihre EXE-Datei löschen, eine Datei durch eine völlig inkompatible ersetzen oder so weiter. Standardmäßige Shader, Materialien, Netze usw. sind nützlich zum Debuggen und Testen, und Sie möchten sie unbedingt, aber betonen Sie nicht zu sehr, dass die Dinge perfekt funktionieren, wenn die erforderlichen Daten von dummen Endbenutzern törichterweise zerstört wurden.
Sean Middleditch
@ Seanmiddleditch Das ist eine Sichtweise. Ich denke, ich kann den Fehlerprüfcode nach #ifdef _DEBUG verschieben?
Dreta
Mein Punkt war mehr, nicht Tonnen von ausgefallenem Selbsterhaltungscode zu schreiben, nicht dass dieser Code nur im Debug-Modus sein sollte. Es gibt eine großartige dreiteilige Artikelserie auf dem Blog bitsquid.se über die Behandlung von Fehlern in Spielen, bitsquid.blogspot.com/2012/01/…
Sean Middleditch

Antworten:

5

Überprüfen Sie Abschnitt 2.11 der OpenGL 4.2-Spezifikation :

Der ausführbare Code für eine einzelne Shader-Stufe stammt aus dem aktuellen Programm für diese Stufe. Wenn ein aktuelles Programmobjekt von UseProgram erstellt wurde, wird dieses Programm für alle Phasen als aktuell betrachtet. Andernfalls wird das an die entsprechende Stufe des Pipeline-Objekts gebundene Programm als aktuell betrachtet, wenn ein gebundenes Programm-Pipeline-Objekt vorhanden ist (siehe Abschnitt 2.11.4). Wenn es kein aktuelles Programmobjekt oder gebundenes Programm-Pipeline-Objekt gibt, ist für keine Stufe ein Programm aktuell. Das aktuelle Programm für eine Stufe gilt als aktiv, wenn es ausführbaren Code für diese Stufe enthält. Andernfalls wird für diese Phase kein Programm als aktiv angesehen. Wenn für die Vertex- oder Fragment-Shader-Stufen kein aktives Programm vorhanden ist, sind die Ergebnisse der Vertex- und / oder Fragmentverarbeitung nicht definiert.Dies ist jedoch kein Fehler. Wenn kein aktives Programm für die Stufen Tessellation Control, Tessellation Evaluation oder Geometry Shader vorhanden ist, werden diese Stufen ignoriert.

Dies ist "undefiniertes Verhalten", das sich pro Implementierung unterschiedlich verhält.

Was die Lösung betrifft, um keinen Shader finden zu können, habe ich festgestellt, dass es am besten ist, sowohl einen Vertex- als auch einen Fragment-Shader fest zu codieren und vor allem anderen zu laden und ihn dann anstelle fehlender Shader zu verwenden. Auf diese Weise werden viele der von mir definierten Uniformen (Transformationsmatrizen) immer noch durchlaufen und das Ergebnis wird halbwegs anständig aussehen. So mache ich es zum Beispiel (C #):

string defaultVertShaderText = 
    @"#version 120

    attribute vec4 in_vertex;

    uniform mat4 model;
    uniform mat4 view;
    uniform mat4 projection;

    void main()
    {
        gl_Position = projection * view * model * in_vertex;
    }";

string defaultFragShaderText =
    @"#version 120

    void main()
    {
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }";

Auch das Hardcodieren einer Standardtextur ist eine gute Idee.

Robert Rouhani
quelle
Danke für die Antwort. Und ich habe über die gleiche Art und Weise nachgedacht, mit leeren Shadern umzugehen, nur dass ich es auf Programmmanagerebene mache (wenn das Programm falsch ist, verwende ich ein Standardprogramm), also muss ich nur meine Standard-Shader für das Programm verwenden Kompilierungsphase und es wird funktionieren, ja. Danke noch einmal.
Dreta
@Robert: -1: Bitte übergeben Sie keine Arrays solcher Zeichenfolgen an OpenGL. glShaderSource Dauert Arrays, aber das ist nicht , wie diese Funktionalität genutzt werden soll. Jede Zeichenfolge ist nicht als separate Zeile gedacht.
Nicol Bolas
@NicolBolas Ich erwähnte, dass ich es in meinem Spiel in C # so mache. OpenTK bietet eine Überladung regulärer GL-Aufrufe, um mit C # -Typen so zu interagieren, wie Sie es natürlich erwarten würden. Soweit ich getestet habe (ATI, AMD, NVIDIA, Intel, Apple und Mesa zwischen Windows, Linux und OS X), kompilieren, verknüpfen und arbeiten die Shader ordnungsgemäß und fehlerfrei. Ich denke, wenn Sie es vorziehen, könnte ich "So mache ich das (C #)" entfernen und es für das gleiche durch das C-System ersetzen.
Robert Rouhani
@ RobertRouhani: Das hat nichts mit der "C-Syntax" zu tun. Es geht um die Tatsache, dass Sie ein Array erstellen. Es sollte eine einzelne Zeichenfolge sein , kein Array von Zeichenfolgen (oder ein Array von einer Zeichenfolge). Ja, es kompiliert, verknüpft und funktioniert ordnungsgemäß und fehlerfrei. Aber es ist immer noch der falsche Weg, die Tatsache zu verwenden, dass glShaderSourceeine Reihe von Zeichenfolgen erforderlich sind.
Nicol Bolas
2
@ RobertRouhani: Es ist falsch, weil das Array nicht dafür gedacht ist. Es sind eine Reihe von Zeichenfolgen erforderlich, damit Sie einen kompilierten Shader effektiv aus Teilen erstellen können, die aufeinander verweisen. Und damit Sie Header-Präfixe für Shader zusammenstellen können, damit diese mit verschiedenen Versionen arbeiten können #ifdef. Es ist nicht vorhanden, sodass Sie jede Zeile als separate Zeichenfolge senden können. Ja, es funktioniert, aber es ist ein schrecklicher Codierungsstil. Es ist wie beim Erstellen einer CPP-Datei, indem jede Zeile einzeln eingeschlossen wird . Ja, es wird kompiliert, aber so verwenden Sie #include nicht.
Nicol Bolas