So you can create a fullscreen quad (ColorRect
with anchors set to wide), and put a shader material on it.
With a shader, blur is done by looking 4 neighbors of the current pixel and averaging them:
shader_type canvas_item;
void fragment() {
vec2 ps = SCREEN_PIXEL_SIZE;
vec4 col0 = texture(SCREEN_TEXTURE, SCREEN_UV + vec2(-ps.x, 0));
vec4 col1 = texture(SCREEN_TEXTURE, SCREEN_UV + vec2(ps.x, 0));
vec4 col2 = texture(SCREEN_TEXTURE, SCREEN_UV + vec2(0, -ps.y));
vec4 col3 = texture(SCREEN_TEXTURE, SCREEN_UV + vec2(0, ps.y));
COLOR = 0.25 * (col0 + col1 + col2 + col3);
}
To achieve glow, instead of just sampling SCREEN_TEXTURE colors, you would subtract a threshold to them, called hdr_threshold
(negative result must be clamped to zero). The lower this threshold, the more glow will happen on less bright objects.
Then you add that to the normal pixel color instead of replacing it:
shader_type canvas_item;
vec4 sample_glow_pixel(sampler2D tex, vec2 uv) {
float hdr_threshold = 1.0; // Pixels with higher color than 1 will glow
return max(texture(tex, uv) - hdr_threshold, vec4(0.0));
}
void fragment() {
vec2 ps = SCREEN_PIXEL_SIZE;
// Get blurred color from pixels considered glowing
vec4 col0 = sample_glow_pixel(SCREEN_TEXTURE, SCREEN_UV + vec2(-ps.x, 0));
vec4 col1 = sample_glow_pixel(SCREEN_TEXTURE, SCREEN_UV + vec2(ps.x, 0));
vec4 col2 = sample_glow_pixel(SCREEN_TEXTURE, SCREEN_UV + vec2(0, -ps.y));
vec4 col3 = sample_glow_pixel(SCREEN_TEXTURE, SCREEN_UV + vec2(0, ps.y));
vec4 col = texture(SCREEN_TEXTURE, SCREEN_UV);
vec4 glowing_col = 0.25 * (col0 + col1 + col2 + col3);
COLOR = vec4(col.rgb + glowing_col.rgb, col.a);
}
However this alone is too subtle, as the blur only spans the neighbor pixels in a radius of 1, which is not much. We could sample more pixels around, but that would become very expensive.
So what we can do is to exploit textureLod
on a lower mipmap. The advantage here is speed, because in that case the mipmaps of the texture combined with the bilinear filter work on hardware level in a single sample, effectively blurring the result.
So we can replace the little function above by this, for example using LOD 2:
vec4 sample_glow_pixel(sampler2D tex, vec2 uv) {
float hdr_threshold = 0.1; // Exagerated, almost everything will glow
return max(textureLod(tex, uv, 2.0) - hdr_threshold, vec4(0.0));
}
And you will notice quite a big difference 
You could even get rid of the 4-sampling, since that’s also working in a single call.
Then you can also compose multiple LOD levels to tweak how glow is spreading.
And you will notice… that Godot may be using a very similar approach, since some glow parameters I talked about are present in WorldEnvironment

Zylann | 2018-07-23 22:03