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):
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;
}