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.

