Animated Starfield

Create a ShaderMaterial with the shader and assign it to your mesh.

  • background_color: The color of the background.
  • aspect_ratio: The amount to stretch stars inversely.
  • density: The amount of stars.
  • layer_parascale: The number of star layers.
  • star_speed: The speed that stars move across the background.
  • star_wave: The speed that stars move back-and-forth across the background.
  • star_rotate_speed: The speed that stars rotate.
  • twinkle_effect: The amount of star flickering.
  • twinkle_speed: The speed of star flickering.
  • pixelate_enabled: Whether to enable the pixelation effect.
  • pixelate_count: How many pixels to round to in the pixelation effect.

To use the shader on a canvas item:

  • Replace:
  • shader_type spatial;
  • render_mode depth_prepass_alpha;
  • render_mode diffuse_toon;
  • render_mode specular_toon;
  • With:
  • shader_type canvas_item;
  • Replace:
  • ALBEDO = color;
  • ALPHA = 1.0;
  • With:
  • COLOR = vec4(color, 1.0);

See on GitHub: https://github.com/Joy-less/AnimatedStarfieldShader

Based on xcv145336’s shader: https://godotshaders.com/shader/2d-starfield-in-the-sky-with-scrolling-effect-ver-1-0

Shader code
shader_type spatial;
render_mode depth_prepass_alpha;
render_mode diffuse_toon;
render_mode specular_toon;

uniform vec3 background_color : source_color = vec3(0.02, 0.04, 0.12);
uniform float aspect_ratio = 1.0;
uniform float density : hint_range(0.0, 3.0, 0.0001) = 1.3;
uniform int layer_parascale : hint_range(0, 5, 1) = 4;
uniform vec2 star_speed = vec2(0.0);
uniform vec2 star_wave = vec2(0.0);
uniform float star_size : hint_range(0.0, 100.0, 0.01) = 3.0;
uniform float star_rotate_speed : hint_range(-3.0, 3.0) = 0.5;
uniform float twinkle_effect : hint_range(0.0, 1.0, 0.01) = 0.6;
uniform float twinkle_speed : hint_range(0.0, 100.0, 0.01) = 0.3;
uniform bool pixelate_enabled = false;
uniform float pixelate_count = 1000.0;

float one_div_x(float x) {
    return (abs(x) < 0.0001) ? 1.0 : (1.0/x);
}

float one_div_x2(float x) {
    return one_div_x(x) * one_div_x(x);
}

float get_beta_w(float x, float f, float size) {
    return size * x * PI / f;
}

float get_beta_h(float y, float f, float size) {
    return size * y * PI / f;
}

float get_i(vec2 uv, float f, vec2 SIZE) {
    return one_div_x2(get_beta_w(uv.x, f, SIZE.x)) * one_div_x2(get_beta_h(uv.y, f, SIZE.y));
}

// Random function
float random(vec2 st) {
    return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43759.5453123);
}

vec2 get_snowflake_world_center(vec2 grid_id, vec2 time_offset, vec2 snow_offset, float layer_scale) {
    return (grid_id + snow_offset + time_offset) / layer_scale;
}

vec2 rotate(vec2 uv, float add_theta) {
    float theta = atan(uv.y, uv.x) + add_theta;
    float r = length(uv);
    return vec2(r * cos(theta), r * sin(theta));
}

void fragment() {
    vec3 color = background_color;

    vec2 st = UV;
	if (pixelate_enabled) {
		st = round(st * pixelate_count) / pixelate_count;
	}
    st.x *= aspect_ratio;
    vec2 cuv = (st - 0.5) * 2.0;

    // Multi-layer star effect
    for (int layer = 0; layer < layer_parascale; layer++) {
        float layer_scale = exp(float(layer + 1) * density);
        vec2 layer_speed = vec2(star_speed.x, star_speed.y) * (1.0 + float(layer) * 0.3);
        float layer_size = star_size * (1.0 - float(layer) * 0.2);

        vec2 layer_st = st * layer_scale;
        vec2 cuv_st = cuv;

        vec2 time_offset = TIME * layer_speed;
        layer_st -= time_offset;

        vec2 grid_st = fract(layer_st);
        vec2 grid_id = floor(layer_st);

        float rand_seed = random(grid_id);

        vec2 snow_pos = vec2(
            0.5 + (0.3 * sin((rand_seed * 6.28) + (TIME * star_wave.x))),
            0.5 + (0.2 * cos((rand_seed * 12.56) + (TIME * star_wave.y)))
        );

        float dist = distance(grid_st, snow_pos);
        float snow_size = layer_size * 0.01 * (0.5 + 0.5 * rand_seed);
        float brightness = 1.0 - (float(layer) * 0.3);

        float m = exp((-dist * dist) / (snow_size * snow_size));

        vec2 fst = cuv_st - (get_snowflake_world_center(grid_id, time_offset, snow_pos, layer_scale) - 0.5) * 2.0;
		fst = rotate(fst, TIME * star_rotate_speed);

        float snowflake = m * 0.5 * (get_i(fst, 0.8 - (dist * 42.0), vec2(1.0 / snow_size)) + 1.0);

        // Twinkle effect
        float twinkle = (1.0 - twinkle_effect) + twinkle_effect * (sin(rand_seed * 100.0 + TIME * twinkle_speed) * cos(rand_seed * 120.0 + TIME * (twinkle_speed + 2.0)));
        snowflake *= twinkle;

        vec3 color_type = vec3(random(grid_id - 3.0), random(grid_id + 7.0), random(grid_id + 5.0));

        color = max(color + (snowflake * brightness) * color_type, color);
    }

    ALBEDO = color;
    ALPHA = 1.0;
}
Tags
galaxy, night, sky, space, Starfield, Stars
The shader code and all code snippets in this post are under MIT license and can be used freely. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from Joyless

Blood Mix Shader

Manga Shader

Related shaders

Starfield

Starfield with Parallax Scrolling Effect

[2D] Starfield in the sky with scrolling effect ver 1.0

guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments