Bloom post processing for viewports

Hello!

This shader targets bloom (or HDR glow) post processing for viewports being rendered in ViewportTextures. This is a “correctly” way to sample HDR pixels without interference from neighbour pixels (this happens with textureLod).

How to use it:

Apply a ShaderMaterial with this shader in a TextureRect with a ViewportTexture into it. The properties of this shader are:

-bloomRadius: As the name sugests, is the radius of the glow, mainly being the distance from the fragment pixel to the neighbour pixels to be sampled;

-bloomThreshold: It’s the minimum color value that will show a glow, defaults to 1.0 so any color with RGB values higher than 1.0 will create the glow;

-bloomIntensity: It’s a value that multiplies the glow color, increasing the brightness of the glow (helps to compensate the threshold).

The way it works is relatively simple:

-Sample from 8 neighbour pixels of the current fragment pixels (offseted by the bloom radius), for each sample, it has a weight that influence more or less the final result (pixels at diagonal influence less than pixels at horizontal and vertical).

-The sampled pixel is subtracted by the bloom threshold, so only pixels with color values higher than 1.0 (or custom threshold) composes the final glow.

-The sample is blurred using custom bilinear filtering, giving a better look to the final result.

-The final glow color, multiplied by the glow intensity, is added with the original pixel color, giving the final result.

It is designed for performance and not quality, so you can use it for mobile projects, for example.

Sadly, it only works for GLES3 as it supports HDR color, not being useful in GLES2 unless you set a threshold that’s lower than 1.0.

Any feedback is really appreciated!

Shader code
shader_type canvas_item;

uniform float bloomRadius = 1.0;
uniform float bloomThreshold = 1.0;
uniform float bloomIntensity = 1.0;

vec3 GetBloomPixel(sampler2D tex, vec2 uv, vec2 texPixelSize) {
	vec2 uv2 = floor(uv / texPixelSize) * texPixelSize;
	uv2 += texPixelSize * .001;
	vec3 tl = max(texture(tex, uv2).rgb - bloomThreshold, 0.0);
	vec3 tr = max(texture(tex, uv2 + vec2(texPixelSize.x, 0.0)).rgb - bloomThreshold, 0.0);
	vec3 bl = max(texture(tex, uv2 + vec2(0.0, texPixelSize.y)).rgb - bloomThreshold, 0.0);
	vec3 br = max(texture(tex, uv2 + vec2(texPixelSize.x, texPixelSize.y)).rgb - bloomThreshold, 0.0);
	vec2 f = fract( uv / texPixelSize );

	vec3 tA = mix( tl, tr, f.x );
	vec3 tB = mix( bl, br, f.x );

	return mix( tA, tB, f.y );
}

vec3 GetBloom(sampler2D tex, vec2 uv, vec2 texPixelSize) {
	vec3 bloom = vec3(0.0);
	vec2 off = vec2(1) * texPixelSize * bloomRadius;
	bloom += GetBloomPixel(tex, uv + off * vec2(-1, -1), texPixelSize * bloomRadius) * 0.292893;
	bloom += GetBloomPixel(tex, uv + off * vec2(-1, 0), texPixelSize * bloomRadius) * 0.5;
	bloom += GetBloomPixel(tex, uv + off * vec2(-1, 1), texPixelSize * bloomRadius) * 0.292893;
	bloom += GetBloomPixel(tex, uv + off * vec2(0, -1), texPixelSize * bloomRadius) * 0.5;
	bloom += GetBloomPixel(tex, uv + off * vec2(0, 0), texPixelSize * bloomRadius) * 1.0;
	bloom += GetBloomPixel(tex, uv + off * vec2(0, 1), texPixelSize * bloomRadius) * 0.5;
	bloom += GetBloomPixel(tex, uv + off * vec2(1, -1), texPixelSize * bloomRadius) * 0.292893;
	bloom += GetBloomPixel(tex, uv + off * vec2(1, 0), texPixelSize * bloomRadius) * 0.5;
	bloom += GetBloomPixel(tex, uv + off * vec2(1, 1), texPixelSize * bloomRadius) * 0.292893;
	bloom /= 4.171573f;
	return bloom;
}

void fragment() {
	vec4 col = texture(TEXTURE, UV);
	vec3 bloom = GetBloom(TEXTURE, UV, TEXTURE_PIXEL_SIZE);
	col.rgb += bloom * bloomIntensity;
	COLOR = col;
}
Tags
glow, HDR, Viewport Shader

More from GhSoares

Raymarching with depth writting

Related shaders

PS1 Post-processing

Post Effect Outline Shader

The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.
guest
0 Comments
Inline Feedbacks
View all comments