Wie kann ich FXAA-Unschärfe entgegenwirken?

7

FXAA ist eine kostengünstige Anti-Aliasing-Methode für die Nachbearbeitung, die sich hervorragend für verzögerte Pipelines eignet, bei denen herkömmliche MSAA möglicherweise keine praktikable Option ist. Es ist auch einfacher zu implementieren als Alternativen wie SMAA.

Wie eine schnelle Google-Suche zeigen wird, hassen es leider viele PC-Spieler, denn selbst wenn der Shader auf die besten Qualitätseinstellungen eingestellt wird, wird das ausgegebene Bild leicht verwischt. Dies macht sich insbesondere dann bemerkbar, wenn eine Szene Texturen mit feinen Details und / oder kleinen Glanzlichtern aufweist.

Gibt es eine Möglichkeit, dieser Unschärfe entgegenzuwirken, ohne wieder ein signifikantes Aliasing einzuführen?

Fibbles
quelle

Antworten:

5

Ausgehend von einem Grafikdesign-Hintergrund besteht eine Lösung, die ich normalerweise verwende, um verschwommene Bilder schärfer erscheinen zu lassen, darin, Rauschen zu überlagern. Das Hinzufügen von zufälligem Rauschen sieht natürlich nicht gut aus. Das Geräusch muss für das zugrunde liegende Material relevant sein. Der klassische Fall besteht darin, ein Material in Photoshop verwittert aussehen zu lassen, indem Grunge-Texturen verwendet und ihre Ebenen so eingestellt werden, dass sie sich vermehren / ausweichen / brennen / etc.

Wie nehmen wir also das vom FXAA-Shader ausgegebene Bild und extrahieren relevantes Rauschen? Auch hier hat Photoshop die Antwort. Einige der ältesten Effekte im Programm wie Unschärfe, Schärfen, Kantenextraktion und Prägen werden mit sogenannten Faltungsmatrizen erzeugt.

Die Matrix, an der wir interessiert sind, hat verschiedene Namen, obwohl ich sie immer als "Kantenextrakt" bezeichnet habe und sie so aussieht:

 0, 0, 0
-1, 1, 0
 0, 0, 0

Um diesen Effekt anzuwenden, möchten wir die Ausgabe des FXAA-Shaders als Eingabe verwenden und ein Vollbild-Quad rendern. Dann werden im Fragment-Shader 9 Texel mit der Fragmentposition in der Mitte abgetastet. Multiplizieren Sie jede abgetastete Farbe mit der entsprechenden Zahl in der Matrix und addieren Sie dann alle diese multiplizierten Abtastwerte. Unten finden Sie einen in GLSL geschriebenen Beispielfragment-Shader, der dies erreicht:

#version 330 core
out vec4 out_colour;
uniform sampler2D blit_map;
uniform vec2 screen_size;

void main()
{
    vec4 sum = vec4(0.0);
    vec2 offset[9] = vec2[](vec2(-1.0, 1.0), vec2(0.0, 1.0), vec2(1.0, 1.0), 
                            vec2(-1.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 0.0), 
                            vec2(-1.0, -1.0), vec2(0.0, -1.0), vec2(1.0, -1.0));
    float kernel[9] = float[](0.0, 0.0, 0.0, 
                             -1.0, 1.0, 0.0, 
                              0.0, 0.0, 0.0);

    for (int i = 0; i < 9; i++)
    {
        vec4 colour = texture2D(blit_map, (gl_FragCoord.xy + offset[i]) / screen_size);
        sum += colour * kernel[i];
    }

    out_colour = sum;
}

Wenn alles richtig läuft, sollten Sie am Ende etwas haben, das so aussieht (das Originalbild war ein hoch kartiertes Grasgelände mit einigen Bäumen): Gelände und einige Bäume.

Wir haben jetzt ein Rauschen, das für unser zugrunde liegendes Bild relevant ist. Fügen Sie es also wieder zu der ursprünglichen Ausgabe hinzu, die wir vom FXAA-Shader erhalten haben.

#version 330 core
out vec4 out_colour;
uniform sampler2D blit_map;
uniform vec2 screen_size;

void main()
{
    vec4 sum = vec4(0.0);
    vec2 offset[9] = vec2[](vec2(-1.0, 1.0), vec2(0.0, 1.0), vec2(1.0, 1.0), 
                            vec2(-1.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 0.0), 
                            vec2(-1.0, -1.0), vec2(0.0, -1.0), vec2(1.0, -1.0));
    float kernel[9] = float[](0.0, 0.0, 0.0, 
                             -1.0, 1.0, 0.0, 
                              0.0, 0.0, 0.0);

    for (int i = 0; i < 9; i++)
    {
        vec4 colour = texture2D(blit_map, (gl_FragCoord.xy + offset[i]) / screen_size);
        sum += colour * kernel[i];
    }

    float sharpen_amount = 0.25;
    out_colour = (sum * sharpen_amount) + texture2D(blit_map, gl_FragCoord.xy / screen_size);
}

Sie werden feststellen, dass wir die Summe auch skaliert haben, bevor wir sie zur Originalfarbe hinzugefügt haben. Die durch FXAA verursachte Unschärfe ist subtil, daher sollte das Schärfen auch subtil sein, sodass Sie den Schärfungsbetrag niedrig halten möchten.

Sie haben wahrscheinlich schon bemerkt, dass dieser Shader nicht so billig ist, wie er sein könnte. Es gibt neun Textur-Lookups, von denen die meisten dem endgültigen Bild nichts hinzufügen, da sie mit Null multipliziert werden. Lassen Sie uns als letzten Schritt den Shader optimieren.

#version 330 core
out vec4 out_colour;
uniform sampler2D blit_map;
uniform vec2 screen_size;

void main()
{
    vec4 colour = texture2D(blit_map, gl_FragCoord.xy / screen_size);
    vec4 sum = colour + (texture2D(blit_map, (gl_FragCoord.xy + vec2(-1.0, 0.0)) / screen_size) * -1.0);

    out_colour = (sum * 0.25) + colour;
}
Fibbles
quelle