Quick answer: Convert FRAGCOORD to UV via division by VIEWPORT_SIZE. Use SCREEN_PIXEL_SIZE for pixel-grain offsets.
Custom blur shader sampling FRAGCOORD shows seams between adjacent pixels. Half-pixel offset misaligns sampling.
The Fix
// Convert to UV correctly
vec2 uv = FRAGCOORD.xy / VIEWPORT_SIZE;
// Pixel-precise neighbor sample
vec2 px = SCREEN_PIXEL_SIZE;
vec3 n = (
texture(SCREEN_TEXTURE, uv + vec2( px.x, 0.0)).rgb +
texture(SCREEN_TEXTURE, uv + vec2(-px.x, 0.0)).rgb +
texture(SCREEN_TEXTURE, uv + vec2(0.0, px.y)).rgb +
texture(SCREEN_TEXTURE, uv + vec2(0.0, -px.y)).rgb
) * 0.25;
VIEWPORT_SIZE division produces 0..1 UV. SCREEN_PIXEL_SIZE provides pixel-step offsets. No half-pixel surprises.
Verifying
Seams gone. 4-tap blur produces smooth output. Sample at FRAGCOORD without conversion: visible grid pattern.
“Divide by VIEWPORT. Pixel size for steps.”
Related Issues
For SCREEN_TEXTURE banding, see SCREEN_TEXTURE. For shader UV flip, see UV flip.
Convert. Pixel size. Smooth.