Screen Space Lens Flare with rainbow colored effect
Hey there,
this is Marcel aka WuotanStudios.
I wanted to share my lens-flare shader i am using in my project RIAD (Reborn in a dungeon).
Godot Version:
4.3-stable, Vulkan-Forward+
It is simple to use:
Step 1: create a new canvas item shader and copy-paste the shader code.
Step 2: Create a Color-Rect node which is visible in the game (best make it a child of the player camera)
Step 3: Make sure that the Color-Rect node is in Full-Rect mode.
Step 4: assign a new material for the Color-Rect and use the shader as material.
Step 5: it already should work
Step 6: play with the value until you have the results you are seeking for.
Shader-Konzept:
It takes the rendered screen, and projects the bright parts of the render into the color-rect.
You can adjust the FlareThreshold value. The higher the value, the less of not so bright screen parts will be used. If the value is to high, nothing will be used as flare -> no flare effect. If the value is too small, you might see too much of the original screen render.
Another important parameter is blur and Thresholdsmoothness, to make the flare “round” and less sharp.
You might need to play around with all values a bit.
Bright levels might create too much weird effects.
I think this shader works better in dark or “less bright” games with a few lightsources / bright elements…
Ideas for the future of this shader:
I consider to use emissive-render,
where only textures and materials with emissive material create the lens-flare effect.
Currently it uses the normal render as basis.
Note:
This is a prototype shader.
There are still things i am not happy with.
Feel free to modify or improve the shader code further, and upload a better version here 😉
For more infos follow me on:
Twitter: https://x.com/WuotanStudios
Bluesky: https://bsky.app/profile/wuotanstudios.bsky.social
Striked: https://striked.gg/app/space/29-riad/news-about-riad
www.wuotanstudios.com
Shader code
//////////////////////////////////////////////////////////////////
//by Marcel Klee - WuotanStudios - 2024
//used in project "RIAD - Reborn in a Dungeon"
//A first person - story driven - action game [WIP]
//
//For more infos follow me on:
//Twitter: https://x.com/WuotanStudios
//Bluesky: https://bsky.app/profile/wuotanstudios.bsky.social
//Striked: https://striked.gg/app/space/29-riad/news-about-riad
//www.wuotanstudios.com
//////////////////////////////////////////////////////////////////
shader_type canvas_item;
render_mode blend_add;
uniform lowp sampler2D Screen_Sample : hint_screen_texture, filter_linear_mipmap_anisotropic;
uniform lowp sampler2D FlareMult;
uniform lowp sampler2D FlareMult2;
uniform float Blur = 2.5; //Blur the Flare after Threshold
uniform float FlareThreshold;
uniform float Thresholdsmoothness = 0.2; //Smoot the Threshold+Blur result
uniform int Flares = 3; //Flare Amount
uniform float FlareSpacing; //Testing-Status
uniform float FlareDistance = 0.5; //Testing-Status
uniform float LensThickness = 1.0; //Testing-Status
uniform float Intensity; //Intensity of the Flare Effect
uniform float Saturation_ = 5.0; // Saturate the lensflare effect
uniform float visibility = 1.0; //Effect visibility by using alpha
uniform float MinFlareIntensity = 0.0; // Minimal brightness
uniform float MaxFlareIntensity = 1.0; // Maximum brightness
uniform float RainbowIntensity = 1.0; // Intensity of rainbow effect
vec3 ApplyThreshold(vec3 color, float threshold){
return color * smoothstep(threshold - Thresholdsmoothness, threshold + Thresholdsmoothness, length(color));
}
vec3 AdjustSaturation(vec3 color, float saturation) {
float gray = dot(color, vec3(0.299, 0.587, 0.114)); // Luminanz-Calculation - NTSC-Standard
return mix(vec3(gray), color, saturation);
}
// Convert from RGB to HSV and back
vec3 RGBToHSV(vec3 color) {
float cmax = max(color.r, max(color.g, color.b));
float cmin = min(color.r, min(color.g, color.b));
float delta = cmax - cmin;
float h = 0.0;
if (delta != 0.0) {
if (cmax == color.r) {
h = mod(((color.g - color.b) / delta), 6.0);
} else if (cmax == color.g) {
h = ((color.b - color.r) / delta) + 2.0;
} else {
h = ((color.r - color.g) / delta) + 4.0;
}
}
h /= 6.0;
float s = (cmax == 0.0) ? 0.0 : delta / cmax;
float v = cmax;
return vec3(h, s, v);
}
vec3 HSVToRGB(vec3 hsv) {
float h = hsv.x * 6.0;
float s = hsv.y;
float v = hsv.z;
int i = int(h);
float f = h - float(i);
float p = v * (1.0 - s);
float q = v * (1.0 - f * s);
float t = v * (1.0 - (1.0 - f) * s);
if (i == 0) return vec3(v, t, p);
else if (i == 1) return vec3(q, v, p);
else if (i == 2) return vec3(p, v, t);
else if (i == 3) return vec3(p, q, v);
else if (i == 4) return vec3(t, p, v);
else return vec3(v, p, q);
}
// Ranbow Effect Function (Hue Shifting)
vec3 ApplyRainbowEffect(vec3 color, float factor) {
vec3 hsv = RGBToHSV(color); // Convert to HSV
hsv.x = mod(hsv.x + factor * RainbowIntensity, 1.0); // Adjust hue based on factor
return HSVToRGB(hsv); // Convert back to RGB
}
void fragment(){
vec2 FlippedUV = vec2(LensThickness) - SCREEN_UV;
vec2 FlareVector = (vec2(FlareDistance) - SCREEN_UV) * FlareSpacing;
vec3 FinalFlare = vec3(0.0);
for (int i = 0; i < Flares; ++i){
vec2 SUV = fract(SCREEN_UV + FlareVector * vec2(float(i)));
float Dist = distance(SUV, vec2(0.5));
float Weight = 1.0 - smoothstep(0.0, 0.75, Dist);
vec3 BlurredScreen = texture(Screen_Sample, SUV, Blur).rgb;
BlurredScreen = ApplyThreshold(BlurredScreen, FlareThreshold); //smoothing the screen (the blur effect can make hard edges)
FinalFlare += BlurredScreen * Weight;
}
FinalFlare *= texture(FlareMult, SCREEN_UV).rgb;
FinalFlare *= texture(FlareMult2, SCREEN_UV).rgb;
FinalFlare *= Intensity;
FinalFlare = clamp(FinalFlare, vec3(MinFlareIntensity), vec3(MaxFlareIntensity));
COLOR.rgb = FinalFlare;
COLOR.rgb = AdjustSaturation(FinalFlare, Saturation_);
// Rainbow Effect based on the Flare-Position
float factor = distance(SCREEN_UV, vec2(0.5)); // Factor can be based on the distance from the center or any other property
FinalFlare = ApplyRainbowEffect(FinalFlare, factor);
COLOR.rgb = FinalFlare;
COLOR.a = visibility;
}
Just wanted to say, you’re trying to apply a Spatial (3D) shader to a Canvas Rect (2D) node, that doesn’t work
Thanks!
When i uploaded the shader it was very late in the night….
i’ve uploaded the wrong shader …… geeeez
Now the correct shader code is is available, thanks to your comment