I was implementing a kind of defocus blur transition effect between two UI screens and came up with the following shader:
/// viewport resolution (in pixels) in .xy and inverse resolution in .zw uniform vec4 u_Resolution; /// 0...1 uniform float u_TransitionValue; /// virtual pixel size uniform float u_PixelSize; uniform sampler2D Texture0; uniform sampler2D Texture1; out vec4 out_FragColor; void main(void) { float T = u_TransitionValue; float S0 = 1.0; float S1 = u_PixelSize; float S2 = 1.0; // 2 segments, 1/2 each float Half = 0.5; float PixelSize = ( T < Half ) ? mix( S0, S1, T / Half ) : mix( S1, S2, (T-Half) / Half ); vec2 D = PixelSize * u_Resolution.zw; vec2 UV = (gl_FragCoord.xy * u_Resolution.zw); // 12-tap Poisson disk coefficients: // https://github.com/spite/Wagner/blob/master/fragment-shaders/poisson-disc-blur-fs.glsl const int NumTaps = 12; vec2 Disk[NumTaps]; Disk[0] = vec2(-.326,-.406); Disk[1] = vec2(-.840,-.074); Disk[2] = vec2(-.696, .457); Disk[3] = vec2(-.203, .621); Disk[4] = vec2( .962,-.195); Disk[5] = vec2( .473,-.480); Disk[6] = vec2( .519, .767); Disk[7] = vec2( .185,-.893); Disk[8] = vec2( .507, .064); Disk[9] = vec2( .896, .412); Disk[10] = vec2(-.322,-.933); Disk[11] = vec2(-.792,-.598); vec4 C0 = texture( Texture0, UV ); vec4 C1 = texture( Texture1, UV ); for ( int i = 0; i != NumTaps; i++ ) { C0 += texture( Texture0, Disk[i] * D + UV ); C1 += texture( Texture1, Disk[i] * D + UV ); } C0 /= float(NumTaps+1); C1 /= float(NumTaps+1); out_FragColor = mix( C0, C1, T ); }
I get wrong results with this fragment shader on some mobile GPUs. For example, Andreno 330 on LG Nexus 5 gives just a series of shifted images instead of blur. Google Nexus 10 runs it just fine. Anyway, I will have to investigate it a bit later.