Woher weiß der Fragment-Shader, welche Variable für die Farbe eines Pixels verwendet werden soll?

82

Ich sehe viele verschiedene Fragment-Shader.

#version 130

out vec4 flatColor;

void main(void)
{
    flatColor = vec4(0.0,1.0,0.0,0.5);
}

Und alle verwenden eine andere Variable für die "Out-Farbe" (in diesem Fall) flatColor ). Woher weiß OpenGL, was Sie tun möchten?

Ich vermute, das funktioniert, weil flatColordie einzige Variable definiert ist als out, aber Sie dürfen weitere outVariablen hinzufügen , nicht wahr? Oder würde das einfach abstürzen?


Eigentlich habe ich als Test gerade Folgendes ausgeführt:

#version 330

in vec2 TexCoord0;

uniform sampler2D TexSampler;

out vec4 x;
out vec4 y;

void main()
{
    y = texture2D(TexSampler, TexCoord0.xy);
}

Es hat gut funktioniert, ob ich verwendet habe xoder y.


Darüber hinaus haben wir eine vordefinierte gl_FragColor. Was ist der Unterschied und warum bestehen die Leute normalerweise darauf, ihre eigenen Variablen zu verwenden?

mpen
quelle

Antworten:

142

Darüber hinaus haben wir eine vordefinierte gl_FragColor.

Beginnen wir damit. Nein, Sie haben das nicht vordefiniert gl_FragColor. Das wurde aus OpenGL 3.1 und höher entfernt. Es sei denn, Sie verwenden Kompatibilität (in diesem Fall sollten Ihre 3.30-Shader sagen#version 330 compatibility oben stehen), sollten Sie diese niemals verwenden.

Zurück zu den benutzerdefinierten Fragment-Shader-Ausgaben. Aber zuerst eine kurze Analogie.

Erinnern Sie sich, wie Sie in Vertex-Shadern Eingaben haben? Und diese Eingänge repräsentieren Vertexattribut - Indizes, die Zahlen , die Sie passieren zu glVertexAttribPointerund glEnableVertexAttribArrayund so weiter? Sie legen fest, welche Eingabe von welchem ​​Attribut abgerufen wird. In GLSL 3.30 verwenden Sie diese Syntax:

layout(location = 2) in color;

Dadurch wird festgelegt, dass die colorVertex-Shader-Eingabe von Attributposition 2 stammt. Vor 3.30 (oder ohne ARB_explicit_attrib_location) müssten Sie dies entweder explizit mit einrichten, glBindAttrbLocationbevor Sie das Programm verknüpfen, oder das Programm nach dem Attributindex abfragen glGetAttribLocation. Wenn Sie keinen expliziten Attributspeicherort angeben, weist GLSL einen Speicherort willkürlich zu (dh auf implementierungsdefinierte Weise).

Das Einstellen im Shader ist fast immer die bessere Option.

In jedem Fall funktionieren Fragment-Shader-Ausgaben fast genauso. Fragment-Shader können in mehrere Ausgabefarben schreiben , die selbst mehreren Puffern im Framebuffer zugeordnet werden . Daher müssen Sie angeben, welche Ausgabe an welche Fragmentausgabefarbe gesendet wird.

Dieser Prozess beginnt mit dem Wert des Fragmentausgabestandorts. Es ist sehr ähnlich zu den Vertex-Shader-Eingabepositionen eingestellt:

layout(location = 1) out secColor;

Es gibt auch die API-Funktionen glBindFragDataLocationund glGetFragDataLocation, die analog zu glBindAttribLocationund sindglGetAttribLocation .

Wenn Sie keine expliziten Zuweisungen vornehmen, weisen Implementierungen normalerweise eine Ihrer Ausgabevariablen Position 0 zu. Der OpenGL-Standard erfordert dies jedoch nicht dieses Verhalten , sodass Sie sich auch nicht darauf verlassen sollten.

Um fair zu sein, sollte Ihr Programm nicht verknüpft werden können, wenn Sie zwei Ausgaben verwendet haben, die keine unterschiedlichen Ausgabestellen erhalten haben. Was wahrscheinlich passiert ist, war, dass Ihr Compiler den optimiert hat, an den Sie nicht geschrieben haben, sodass er es irgendwie vergessen hat, als es an der Zeit war, nach Linkerfehlern zu suchen.

Nicol Bolas
quelle
3
Ich wünschte, ich könnte mehr als +1 geben. Spektakuläre Antwort. Ich habe mich gefragt, wie Fragmentausgabestellen funktionieren. :)
Kevintodisco
Ahh .. also sind sie veraltet gl_FragColor, um Ihnen mehr Flexibilität bei der Auswahl des Puffers zu geben, in den Sie schreiben möchten? Macht Sinn. Danke noch einmal!
Mpen
3
@Mark: "Veraltet" bedeutet "Sie können es weiterhin verwenden, es kann jedoch in späteren Versionen entfernt werden." "Entfernt" bedeutet entfernt . Da ist ein Unterschied. Das, was in GL 3.0 als veraltet markiert war , wurde in 3.1 entfernt (abgesehen von ein paar Dingen).
Nicol Bolas
3
@NicolBolas: "GL 3.3 und höher (dies ist eine Änderung gegenüber früheren Versionen) weisen sie alle Position 0 zu." Ich frage mich, wo dies durch die Spezifikation garantiert wird. 3.3 sagt: "Wenn ein Programm verknüpft ist, werden alle variablen Variablen ohne eine [...] angegebene oder explizit im Shader-Text festgelegte Bindung automatisch vom GL an Fragmentfarben und -indizes gebunden. Alle diese Zuweisungen verwenden Farbindizes von Null.". Der letzte Satz ist in 3.2 nicht vorhanden. Allerdings bezieht Index Farbe nicht Zahl , sondern nur auf den Dual - Source - Mischindex (die in 3.3 hinzugefügt wurden).
derhass
1
Danke @NicolBolas, tolle Antwort! Ich habe es also noch nicht ganz verstanden und möchte es einfach "ohne Frage" machen. In unseren Shadern geben wir layout (location = 0) out vec4 outColorbeim Zeichnen immer an, was gebunden ist (meistens "der Bildschirm" und kein FBO). Wenn wir also einen FBO (oder einen anderen Puffer) nicht mit glDrawBuffers gebunden haben und dies angeben location = 0, wird er standardmäßig immer korrekt auf den Bildschirm gezeichnet ?
AzP
9

Ich möchte dies für OpenGLES 3.1 angeben, das den Link GLSL_ES_3.10 verwendet :

§4.4.2

Wenn nur eine einzige Ausgabe [im Fragment-Shader] vorhanden ist, muss der Speicherort nicht angegeben werden. In diesem Fall wird standardmäßig Null verwendet.

Lorenzo Belli
quelle