Lens Flare Shader
This shader was translated and slightly modified from mu6k’s lens flare shader to work in my 3D scene using Godot 4.
The shader requires the sun’s position on the viewport. This can be calculated by using Camera3D.unproject_position.
If you are using a procedural sky with a directional light to simulate a sun, the code in the screenshot might help (I am using Godot 4 so the gdscript code may be a bit different).
Any improvements or fixes are welcome. Even though the shader is MIT licensed, do consider dropping an email to the original author (mu6k).
Shader code
// TRANSLATED & MODIFIED FROM: https://www.shadertoy.com/view/4sX3Rs
shader_type canvas_item;
render_mode blend_mix;
uniform vec2 sun_position = vec2(0.0);
uniform vec3 tint = vec3(1.4,1.2,1.0);
uniform sampler2D noise_texture;
float noise_float(float t, vec2 texResolution)
{
return texture(noise_texture,vec2(t,0.0)/texResolution).x;
}
float noise_vec2(vec2 t, vec2 texResolution)
{
return texture(noise_texture,t/texResolution).x;
}
vec3 lensflare(vec2 uv,vec2 pos, vec2 texResolution)
{
vec2 main = uv-pos;
vec2 uvd = uv*(length(uv));
float ang = atan(main.x,main.y);
float dist = length(main);
dist = pow(dist,0.1);
float n = noise_vec2(vec2(ang*16.0,dist*32.0), texResolution);
// Do not need an artificial sun
//float f0 = 1.0/(length(uv-pos)*16.0+1.0);
//f0 = f0 + f0*(sin(noise_float(sin(ang*2.+pos.x)*4.0 - cos(ang*3.+pos.y), texResolution)*16.)*.1 + dist*.1 + .8);
float f1 = max(0.01-pow(length(uv+1.2*pos),1.9),.0)*7.0;
float f2 = max(1.0/(1.0+32.0*pow(length(uvd+0.8*pos),2.0)),.0)*00.25;
float f22 = max(1.0/(1.0+32.0*pow(length(uvd+0.85*pos),2.0)),.0)*00.23;
float f23 = max(1.0/(1.0+32.0*pow(length(uvd+0.9*pos),2.0)),.0)*00.21;
vec2 uvx = mix(uv,uvd,-0.5);
float f4 = max(0.01-pow(length(uvx+0.4*pos),2.4),.0)*6.0;
float f42 = max(0.01-pow(length(uvx+0.45*pos),2.4),.0)*5.0;
float f43 = max(0.01-pow(length(uvx+0.5*pos),2.4),.0)*3.0;
uvx = mix(uv,uvd,-.4);
float f5 = max(0.01-pow(length(uvx+0.2*pos),5.5),.0)*2.0;
float f52 = max(0.01-pow(length(uvx+0.4*pos),5.5),.0)*2.0;
float f53 = max(0.01-pow(length(uvx+0.6*pos),5.5),.0)*2.0;
uvx = mix(uv,uvd,-0.5);
float f6 = max(0.01-pow(length(uvx-0.3*pos),1.6),.0)*6.0;
float f62 = max(0.01-pow(length(uvx-0.325*pos),1.6),.0)*3.0;
float f63 = max(0.01-pow(length(uvx-0.35*pos),1.6),.0)*5.0;
vec3 c = vec3(.0);
c.r+=f2+f4+f5+f6; c.g+=f22+f42+f52+f62; c.b+=f23+f43+f53+f63;
c = c*1.3 - vec3(length(uvd)*.05);
// Do not need an artificial sun
//c+=vec3(f0);
return c;
}
vec3 cc(vec3 color, float factor,float factor2) // color modifier
{
float w = color.x+color.y+color.z;
return mix(color,vec3(w)*factor,w*factor2);
}
void fragment()
{
vec2 texResolution = 1.0 / TEXTURE_PIXEL_SIZE;
vec2 resolution = 1.0 / SCREEN_PIXEL_SIZE;
vec2 uv = FRAGCOORD.xy / resolution.xy - 0.5;
uv.x *= resolution.x/resolution.y; //fix aspect ratio
vec2 mouse = (sun_position.xy / resolution.xy) - vec2(0.5, 0.5);
mouse.x *= resolution.x / resolution.y; //fix aspect ratio
vec4 previousColor = texture(SCREEN_TEXTURE, SCREEN_UV);
vec3 color = previousColor.rgb;
color += tint * lensflare(uv, mouse.xy, texResolution);
color -= noise_vec2(FRAGCOORD.xy, texResolution)*.015;
color = cc(color,.5,.1);
COLOR = vec4(color,1.0);
}
Hey! I am trying to implement this shader in 2D game. Everything works fine but lens flare position seems to be inverted. When I am to the right from sun, lens flare appears from right (instead of left) and vice versa. Could you help me out?
Hi. Sorry for the late reply – I didn’t receive any notifications for your comment.
Maybe you can share your shader code and we can try to find the issue?
No worries! I already found the solution myself: set scale.x of color rect to -1 🙂
How do I use this? I’ve added a CSGSphere3D and am applying the shader. As a child I have added a directional light. I have also added the code as mentioned to align the sun position to the player camera. All I see in game is the directional light, no shader. What am I missing?
Is not working in Godot 4 beta 3, you have any tutorial?
Is there a way to draw the sun behind objects?
i found a bug with sun position, godot flips screen texture vertically so in fragment function below aspect ratio fixes should be added
uv.y *= -1.0;
otherwise, sun_position.y will be inverted
This was super helpful and it didn’t destroy my computer trying to process it. A fantastic solution.
It’s probably easier to do this with a
shader_type sky
than a
shader_type canvas_item
Then,
dot(EYEDIR, LIGHT0_DIRECTION)
is your sun incidence factor, and you can apply all the needed formulas to it using just these two vectors.
The shadertoy shader also tries to just fake the lensflare from 2D position, but it’s much better (and easier) to do it from 3D. You can circumvent all the pseudo-3D-isation it does with atan, etc.
seems to be some issue with this on the latest version of Godot 4…
SCREEN_TEXTURE was replaced with hint_screen_texture, but im not sure how to implement it
Add the following line of code within your shader:
uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_linear_mipmap;