How To Create Aurora Borealis In Godot 4.6.1?

Godot Version

Godot 4.6.1

Question

Does anyone know how to create aurora borealis and aurora borealis shaders for a 3D game in Godot 4? There seem to be very few tutorials and resources on how to do this.

The only tutorial I’ve seen was on YouTube, but it was only for Godot 3. =(


I’d never create it like this (volumetric raymarching) for a game. This method is computationally very expensive. And doing it volumetrically just makes no sense visually, because aurora is always far away in the sky, you can’t walk around it to observe it from all sides, unless it’s for a magic spell effect… Anyway, just deform a planar mesh and slowly slide some noise texture on it. You can get very close to this look for a fraction of performance cost.

2 Likes

I tried Quad Mesh but the problem is that the edges of the QuadMesh cut off the immersion.

shader_type spatial;
render_mode blend_add, depth_draw_never, cull_disabled, unshaded;

uniform sampler2D noise_tex : hint_default_white;
uniform float time_scale : hint_range(0.0, 2.0) = 0.3;
uniform float edge_fade : hint_range(0.01, 0.5) = 0.15; // largeur du fondu

void fragment() {
vec2 uv = UV;
float t = TIME * time_scale;

// Ondulation verticale
float wave = sin(uv.x * 6.0 + t * 2.0) * 0.05;
uv.y += wave;

float noise = texture(noise_tex, uv * 0.5 + vec2(t * 0.1, 0.0)).r;

// --- Fondu sur les 4 bords ---
float fade_x = smoothstep(0.0, edge_fade, uv.x)           // bord gauche
             * smoothstep(1.0, 1.0 - edge_fade, uv.x);    // bord droit
float fade_y = smoothstep(0.0, edge_fade, uv.y)           // bord bas
             * smoothstep(1.0, 1.0 - edge_fade, uv.y);    // bord haut

float edge_mask = fade_x * fade_y;

// Dégradé vertical supplémentaire (aurore plus dense au centre)
float alpha = smoothstep(0.0, 0.4, uv.y) * (1.0 - smoothstep(0.5, 1.0, uv.y));
alpha *= noise * 1.5;
alpha *= edge_mask; // ✅ applique le masque de bord

// Couleurs aurora
vec3 color_green  = vec3(0.0, 1.0, 0.4);
vec3 color_violet = vec3(0.5, 0.0, 1.0);
vec3 color_cyan   = vec3(0.0, 0.8, 1.0);

vec3 col = mix(color_green, color_violet, smoothstep(0.3, 0.7, noise));
col = mix(col, color_cyan, sin(t + uv.x * 3.0) * 0.5 + 0.5);

ALBEDO = col * COLOR.rgb;
ALPHA  = alpha * COLOR.a;

}

You can fade all edges.

It’s easy enough to convert shaders from Godot 3 to 4. There’s even a guide somewhere in the docs that tells you all the changes between 3 and 4 so that you can update your shaders.

Aurora borealis?
At this time of year?
Entirely localized in your computer?!
Can I see it?

2 Likes

Yes, I saw the Godot 3 shader script. I’ll probably update it for Godot 4.

1 Like

Once you get it working, consider uploading it to the Godot Asset LIbrary, so others can use it. Assumming the original shader code’s license allows it.

1 Like

I tried converting the shader code in Godot 4, but it still doesn’t work.

shader_type spatial;
render_mode blend_add, cull_disabled, depth_draw_never, unshaded;

uniform float speed = 0.01;
uniform vec4 color : source_color;
uniform float emission_strength = 5.0;
uniform sampler2D noise : filter_linear_mipmap, repeat_enable;
uniform float offset = 0.0;
uniform float smoothness = 0.15;
uniform float distort = 1.0;
uniform float scale = 0.02;

group_uniforms Raymarching;
uniform float STEP = 0.01;
uniform float BASE_DENSITY = 2.0;

varying vec3 camera_position;
varying vec3 vertex_position;

float amplify(float value) {
float magic_number = 0.166504;
float output = 0.0;
value = clamp(value, 0.0, 1.0);
value = pow(value, 2.0);
output += pow(magic_number, 4.0) * value;
value = pow(value, 2.0);
output += magic_number * value;
value = pow(value, 2.0);
output += value;
return output;
}

float interpolate_noise(vec2 uv1, vec2 uv2) {
return smoothstep(
-smoothness, smoothness, texture(noise, uv1).r - texture(noise, uv2).r
);
}

float random_wave(vec2 uv) {
vec2 uv_distort = texture(noise, uv).rr * distort * 0.5;
vec2 uv1 = uv * scale + vec2(TIME * speed, TIME * speed - offset) + uv_distort;
vec2 uv2 = uv * scale + vec2(TIME * speed + 0.5, TIME * speed + offset) + uv_distort;
float interpolated_noise = interpolate_noise(uv1, uv2);
float intensity = 0.2 + clamp((0.5 - abs(interpolated_noise - 0.5)) * 1.5, 0.0, 1.0);
return amplify(intensity);
}

float ray_march(vec3 ray_origin, vec3 ray_direction, float start) {
float density = 0.0;
float dist = start + STEP;

// For loop avec max iterations pour éviter les problèmes de driver
for (int i = 0; i < 300; i++) {
	vec3 point = ray_origin + ray_direction * dist;
	if (clamp(point, vec3(-1.0), vec3(1.0)) != point) {
		break;
	}
	vec2 uv = vec2((point.x + 1.0) * 0.5, (point.z + 1.0) * 0.5);
	float wave_value = random_wave(uv);
	float height_factor = (1.0 - point.y) * 0.5;
	density += BASE_DENSITY * STEP * height_factor * wave_value;
	dist += STEP;
}

return density;

}

void vertex() {
vertex_position = VERTEX;
camera_position = (inverse(MODEL_MATRIX) * vec4(INV_VIEW_MATRIX[3].xyz, 1.0)).xyz;
}

void fragment() {
vec3 ray_direction = normalize(vertex_position - camera_position);
float density = ray_march(
camera_position, ray_direction, length(camera_position - vertex_position)
);

ALBEDO = color.rgb * density;
EMISSION = color.rgb * density * emission_strength;
ALPHA = clamp(density, 0.0, 1.0);

}

If you don’t understand what that shader is doing it may not be the wisest thing to try to port it.

Make a regular shader. Model a couple of curved surfaces in Blender and slide several layers of scaled noise over them.

1 Like

I found an example of the Northern Lights on GitHub.

It looks plausible, but you can still see the edge of the quad mesh, and I don’t know how to remove it.

I don’t see any edges on your screenshot. If there are some you need to adjust the shader parameters or enlarge the quad.

Here’s a quick example with sliding the cellular noise texture over some geometry. Not as good as volumetric one but for a price of just a few texture lookups does a decent job.

shader_type spatial;
render_mode blend_add, unshaded, cull_disabled;

uniform sampler2D noise;

float fade_edges(vec2 uv, float left, float right, float up, float down){
	return smoothstep(0.0, up, uv.y) * smoothstep(1.0, 1.0 - down, uv.y) * smoothstep(0.0, left, uv.x) * smoothstep(1.0, 1.0-right, uv.x);;
}

void vertex() {
	VERTEX += NORMAL * sin(VERTEX.x * TAU*0.3 + TIME*0.5) * 0.4;
	VERTEX.y += sin(VERTEX.x * (TAU * 0.2) + TIME) * .1 + sin(VERTEX.x * (TAU *0.5) + TIME) * 0.02;
}

void fragment() {

	vec2 uv = UV;
	uv.y /= 15.0;
	uv.x += TIME * 0.02;	
	uv.x *= 1.5;
	vec2 uv2 = vec2(sin(UV.x) * 0.2, cos(UV.y)) * 0.3;
	uv2.x *= 4.0;
	vec2 uv4 = vec2(UV.x * 14.0, UV.y*0.1 + TIME * 0.1 );

	ALBEDO = mix(vec3(0.3, 1.0, 0.01), vec3(0.7, 0.01, 0.4), pow(1.0 - UV.y, 2.0));
	
	ALPHA = texture(noise, uv).r - 0.1 * texture(noise, uv2).r;
	ALPHA *= fade_edges(UV, 0.2, 0.2, 1.0, 0.1);
	ALPHA *= smoothstep(-0.4, 0.6, texture(noise, UV * vec2(0.2, 0.1) + vec2(TIME*0.05, TIME * 0.001)).r) * 0.4;
	ALPHA += smoothstep(0.5, 0.7, texture(noise, uv4).r) * fade_edges(UV, 0.1, 0.1, 0.8, 0.6) * 0.02;
	ALPHA = clamp(ALPHA, 0.0, 1.0);
	ALPHA *= 1.0 + pow(UV.y, 10.0) * 60.0;
	ALPHA *= 0.7;
	float fresnel =  abs(dot(NORMAL, vec3(0.0, 0.0, 1.0)));
	ALPHA = mix(ALPHA, ALPHA * smoothstep(0.5, 2.0, fresnel), pow(UV.y, 1.0));
}
1 Like

If there’s an edge, the white line that appears on the aurora borealis.

Could you share your example with me? I tried it, and here’s what happened.

The key part is to have proper geometry. It won’t look good on cubes or planes. Make a meandering curve in Blender, extrude it and apply the shader to that. I used several instances of this:

I tried to create a Bézier curve, extrude it, and convert it to a mesh, but once on Godot, it renders strangely and the shader doesn’t seem to work when I apply the script.

You need to assign a noise texture to the shader parameter.
Also export the mesh as obj.

I tried and it still doesn’t work.

In the shader did you set the render_mode to cull_disabled?
If that doesn’t fix it then I got no clue, @normalized and other more experienced ppl should be more helpful.

What happens if you assign a standard unshaded material to the surface?

However, cull disabled is indeed in the shader script.