2014-04-16 13 views
6

Was ist die beste Möglichkeit, Vollbild-Texturen in einem Fragment-Shader, zum Beispiel der G-Puffer in einem verzögerten Renderer, oder die Szene Textur in einem Postprozess-Shader?Der beste Weg, um eine Fullscreen-Textur zu sampeln

Im Moment habe ich die folgenden zwei Arten verwenden:

  • Geben Sie die Bildschirmgröße an den Shader als einheitliche und berechnen (u, v) von gl_FragCoord:

    vec2 texCoord = gl_FragCoord.xy/vScreenSize; 
    vec3 c = texture(tex, texCoord).rgb; 
    

    Does nicht scheinen ideal zu sein, da eine Aufteilung erforderlich ist und die Bereitstellung des Shaders mit der Bildschirmgröße mühsam ist.

  • Convert gl_FragCoord.xy in ein ivec und verwenden texelFetch:

    vec3 c = texelFetch(tex, ivec2(gl_FragCoord.xy), 0).rgb; 
    

    auch nicht ideal, da die Umwandlung von float zu int erforderlich ist.

Also gibt es einen besseren Weg? Ich möchte wirklich nur einen Puffer genau an der Stelle abtasten, an der mein Pixelshader gezeichnet wird.


// EDIT:


Ok, mit den Vorschlägen von interpoliert Textur-Koordinaten, die vom Vertex-Shader kommen ich es geschafft, den folgenden Code, um herauszufinden:

Vertex-Shader:

#version 150 

uniform mat4 im_ModelViewProjectionMatrix; 

in vec3 iv_Vertex; 
noperspective out vec2 vVertCoord; 

void main(void) 
{ 
    gl_Position = im_ModelViewProjectionMatrix * vec4(iv_Vertex, 1.0); 
    vVertCoord = (gl_Position.xy/gl_Position.w) * (0.5) + vec2(0.5); 
} 

Ich berechne grundsätzlich normalisierte Gerätekoordinaten (NDC) aus dem Clip Spac Stellen Sie die Position mit der perspektivischen Division ein und bilden Sie dann die NDCs (die von [-1,1] reichen) in das Intervall [0,1] ab. Dies funktioniert hervorragend für Vollbild-Quads (auch ohne die perspektivische Teilung, weil Koordinaten so einfach sind). Die willkürliche Geometrie, die ich in meinem Deferred Renderer als Lichtgeometrie zeichnen muss, hat jedoch einige Probleme. In dem Vertex-Shader-I-Ausgang vVertCoord als Farbe für Rot = x und grün = y:

#version 150 

noperspective in vec2 vVertCoord; 
out vec4 colorOut; 

void main(void) 
{ 
    colorOut = vec4(vVertCoord.x, vVertCoord.y, 0, 1); 
} 

Dies ist das Ergebnis, wenn ich in einer Punkt-Licht-Kugel bin, alles sieht gut aus (die schwarzen Linien wiedergegeben werden absichtlich):

All fine

wenn ich jedoch näher an der Licht-Geometrie bewegen ist dies das Ergebnis:

Not ok

Was macht der rote Fleck in der oberen linken Ecke dort? Sie möchten die Ergebnisse nicht in Echtfarben sehen, da die Debugging-Farben deaktiviert sind, da es sich um einen LSD-Trip handelt. Wenn Sie die Kamera bewegen, wird alles verzerrt. Berührt sich diese Genauigkeit? Beachten Sie, dass alles in Ordnung ist, wenn ich stattdessen gl_FragCoord im Pixelshader verwende.

+0

Gibt es einen besonderen Grund, warum Ihre Texturkoordinaten auf gl_FragCoord basieren und kein normales interpolierte Attribut? Im ersten Beispiel würde dies die Notwendigkeit für die Teilung bereits beseitigen. p.s. Anstelle von vScreenSize können Sie den reziproken Wert der Bildschirmgröße haben (indem Sie die Division in eine Multiplikation umwandeln). –

+0

Wahrscheinlich möchten Sie das letztere nicht, da dies die Möglichkeit der Strukturfilterung ausschließt. Während Sie vielleicht denken, dass Ihnen das nicht wirklich wichtig ist, kann es in einer Deferred-Shading-Engine nützlich sein, die G-Buffer-Generierung und -Schattierung mit unterschiedlichen Auflösungen durchzuführen (besonders auf Low-End-Hardware, die normalerweise ziemlich voll ist). –

Antwort

2

Sie müssen keine speziellen Berechnungen durchführen, wenn Sie nur eine interpolierte Vertex-Koordinate von Ihrem Vertex-Shader übergeben.

Zum Beispiel, wenn Sie wurden eine einfache Einheitsquadrat Zeichnung, die Ihren Bildschirm bedeckt, können Sie einfach und würde dann den gesamten Bildschirm Textur empfangen, können Sie nur so etwas tun:

vertex shader pseudocode: 
layout(position = 0) in vec3 in_vertex; 
out vec3 out_vertex; 
void main() 
{ 
    //Do your matrix multiplications to your in_vertex to get its final position... 
    out_vertex = in_vertex; 
} 

Ihr Vertex-Shader interpoliert dann den Wert in_vertex korrekt im Bereich x: 0 ... 1, y: 0 ... 1 (solange Sie ein Einheitsquadrat zeichnen) und übergeben ihn an Ihren Fragment-Shader. Ihr Fragment-Shader wird es dann mit etwa so:

fragment shader pseudocode: 
in vec3 out_vertex; 
uniform sampler2D tex; 
void main() 
{ 
    gl_fragcolor = texture(tex,vec2(out_vertex.x,out_vertex.y)); 
} 

Keine anderen Mathematik benötigt wird, solange Sie darauf achten, dass die out_vertex wird immer nur im Bereich von 0 ... 1. Zur Erweiterung dieses Beispiel ein wenig, hier vorstellen, ist unser Platz:

(0,1)+-----------+(1,1) | | | | | | | | | | (0,0)+-----------+(0,1)

Und wir wollten diesen Punkt genau in der Mitte probieren:

(0,1)+-----------+(1,1) | | | | | * | | | | | (0,0)+-----------+(0,1)

Unser Vertex-Shader wird, dass automatisch interpoliert Position von den anderen 4 Positionen und übergeben Sie die folgenden vec3 zum Fragment Shader:

, die die Textur probieren wird dann verwendet, um erfolgreich

+0

Ich glaube, Sie müssen noch 'gl_Position' in Ihrem Vertex-Shader setzen. Wenn Sie ein Einheitsquadrat zeichnen, wie @redsoxfantom vorgeschlagen hat, können Sie etwas wie 'gl_Position = vec4 (2.0 * out_vertex, 0.0) - 1.0;' verwenden. Sie benötigen keine Matrixmultiplikationen. Lassen Sie mich wissen, wenn Sie weitere Erklärungen und Details benötigen. Etwas schwer, es in einem Kommentar zu bedecken, aber ich kann es als Antwort posten. –

+0

Gute Antwort, obwohl ich mit einem anderen Code gelandet bin, weil ich nicht immer Fullscreen-Quads sondern auch Lichtgeometrie zeichne. In diesem Fall sind die Eckpunktkoordinaten nicht von großem Nutzen. – Marius

Verwandte Themen