Why post processing antialiasing ? Because of two reasons. A good and fast post processing filter like fxaa or cmaa can achieve almost the same visual output like the usual multisampling counter part (msaa) and costs way less on the GPU because you dont need multiple samples per fragment/pixel! Another restriction is the rendering pipeline you might choose. A deferred renderer uses multiple framebuffers to achieve multiple different effects and using multiple samples on each of those framebuffers might have a really big impact on performance. Additionally it is practically impossible to use mxaa in a deferred renderer, because multisampling the position framebuffer results in wrong values around objects!
Let’s have a look on the code:
The complete scene is rendered and stored in a framebuffer and bound as a texture (l_tex) for the following shader. The output buffer is bound properly (def_e in the fragment shader)…
//render a full screen quad with vertex coordinates: //-1.0, -1.0, 0.5 //-1.0, 1.0, 0.5 // 1.0, 1.0, 0.5 // 1.0, -1.0, 0.5 //or similar |
//vertex shader, nothing special here... #version 400 layout(location=0) in vec4 vp; void main () { gl_Position = vp; } |
//fragment shader, the interesting part! #version 400 uniform sampler2D l_tex; //the rendered framebuffer containing the color bound as sampler2D uniform vec2 win_size; //the size of the window/framebuffer layout( location = 0 ) out vec4 def_e; //the manipulated color output ////////////////////////////////////////////////////////////////////////////// //FXAA #ifndef FXAA_REDUCE_MIN #define FXAA_REDUCE_MIN (1.0/ 128.0) #endif #ifndef FXAA_REDUCE_MUL #define FXAA_REDUCE_MUL (1.0 / 8.0) #endif #ifndef FXAA_SPAN_MAX #define FXAA_SPAN_MAX 8.0 #endif //optimized version for mobile, where dependent //texture reads can be a bottleneck vec4 fxaa(sampler2D tex, vec2 fragCoord, vec2 resolution) { vec4 color; vec2 inverseVP = 1.0 / resolution.xy; vec2 v_rgbNW = (fragCoord + vec2(-1.0, -1.0)) * inverseVP; vec2 v_rgbNE = (fragCoord + vec2(1.0, -1.0)) * inverseVP; vec2 v_rgbSW = (fragCoord + vec2(-1.0, 1.0)) * inverseVP; vec2 v_rgbSE = (fragCoord + vec2(1.0, 1.0)) * inverseVP; vec2 v_rgbM = vec2(fragCoord * inverseVP); vec3 rgbNW = texture2D(tex, v_rgbNW).xyz; vec3 rgbNE = texture2D(tex, v_rgbNE).xyz; vec3 rgbSW = texture2D(tex, v_rgbSW).xyz; vec3 rgbSE = texture2D(tex, v_rgbSE).xyz; vec4 texColor = texture2D(tex, v_rgbM); vec3 rgbM = texColor.xyz; vec3 luma = vec3(0.299, 0.587, 0.114); float lumaNW = dot(rgbNW, luma); float lumaNE = dot(rgbNE, luma); float lumaSW = dot(rgbSW, luma); float lumaSE = dot(rgbSE, luma); float lumaM = dot(rgbM, luma); float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); mediump vec2 dir; dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP; vec3 rgbA = 0.5 * ( texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + texture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz); vec3 rgbB = rgbA * 0.5 + 0.25 * ( texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + texture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz); float lumaB = dot(rgbB, luma); if ((lumaB < lumaMin) || (lumaB > lumaMax)) color = vec4(rgbA, texColor.a); else color = vec4(rgbB, texColor.a); return color; } //FXAA END ////////////////////////////////////////////////////////////////////////////// void main () { //calculate smoothed color by applying fxaa (usually at sharp edges) def_e.rgb = fxaa(l_tex,gl_FragCoord.st,win_size).rgb; def_e.a = 1.0f; } |
Don’t forget to properly bind your framebuffers and disable the depthtest or clear your depth if you use any when you render the fullscreen quad!
Below’s a screenshot where fxaa is applied (no zoom left and 2x zoomed right):
Source for shader found here: https://github.com/mattdesl/glsl-fxaa