Moving planets in space for Skyboxes

I found this shader on shadertoy : https://www.shadertoy.com/view/XtdGR7 and adapted it to godot for my mobile game Dodgers – Online Spaceshooter. Feel free to download it and try it !

You can use the shader as a skybox by adding a Viewport as a parent of a Sprite and using the viewport texture, like I did, or as a regular texture.

func set_sky():
	var img=get_node("Space").get_viewport().get_texture()
	get_parent().get_parent().get_node("Environment").environment.background_sky.set_panorama(img)

If you want it to move, you have to attach a script to the sprite that updates the shader variable:

extends Sprite

func _process(delta):
	self.material.set("shader_param/time",global.iTime)

And in _process wherever you want :

func _process(delta):
	iTime+=delta

You can obviously adapt it as you want, by changing movement direction and planets type.

Don’t forget to grab the textures in screenshots which are needed for the planets. Also, you can replace the sky one by any sky you want.

Shader code
// Moving planets in space - Clarkia Games
// Adapted from https://www.shadertoy.com/view/XtdGR7
// Modified to remove tunnel and change movement

shader_type canvas_item;

uniform float time;
uniform float SPEED = 10.0;

uniform sampler2D iChannel0;
uniform sampler2D iChannel1;
uniform sampler2D iChannel2;

uniform vec3 sunColor = vec3(3,2,1);
uniform vec3 sunDir = vec3(-0.363696,0.581914,0.727393);//normalize(vec3(-.5,.8,1));

const float PI = 3.1415926535;
const float ARMS = 3.0;
const vec2 EPSILON = vec2(0, .005);

//const vec3 sunColor = vec3(3,2,1);

vec2 rotate(vec2 p, float a)
{
    return vec2(cos(a)*p.x + sin(a)*p.y, -sin(a)*p.x + cos(a)*p.y);
}

float Noise( in vec3 x )
{
    vec3 p = floor(x);
    vec3 f = fract(x);
    f = f*f*(3.0-2.0*f);
    vec2 uv = (p.xy+vec2(37.0,17.0)*p.z) + f.xy;
    vec2 rg = textureLod( iChannel0, (uv+ 0.5)/256.0, 0.0 ).yx;
    return -1.0+2.0*mix( rg.x, rg.y, f.z );
}

float Map3( in vec3 p )
{
    vec3 q = p;
    float f;
    f  = 0.50000*Noise( q ); q = q*2.02;
    f += 0.25000*Noise( q ); q = q*2.03;
    f += 0.12500*Noise( q ); q = q*2.01;
    return f;
}

float Map5( in vec3 p )
{
    vec3 q = p;
    float f;
    f  = 0.50000*Noise( q ); q = q*2.02;
    f += 0.25000*Noise( q ); q = q*2.03;
    f += 0.12500*Noise( q ); q = q*2.01;
    f += 0.06250*Noise( q ); q = q*2.02;
    f += 0.03125*Noise( q );
    return f;
}

mat4 LookAt(vec3 pos, vec3 target, vec3 up)
{
    vec3 dir = normalize(target - pos);
    vec3 x = normalize(cross(dir, up));
    vec3 y = cross(x, dir);
    return mat4(vec4(x, 0), vec4(y, 0), vec4(dir, 0), vec4(pos, 1));
}

vec2 TunnelCenter(float z)
{
    return vec2(0.0);
}

float GetAngle(vec3 pos)
{
    return atan(pos.y,pos.x) - pos.z*.25 + time*3.7 + sin(time)*.2;
}


vec3 Fresnel(vec3 R0, vec3 normal, vec3 viewDir)
{
    return vec3(0);
}

float Map(vec3 pos, float z_offset)
{
    pos.z -= z_offset;
    pos.xy -= TunnelCenter(pos.z);
    return length(pos.xy) - 1.0;
}

vec3 Normal(vec3 pos, float z_offset)
{
    vec2 e = vec2(0, .05);
    return normalize(vec3(Map(pos + e.yxx, z_offset), Map(pos + e.xyx, z_offset), Map(pos + e.xxy, z_offset)) - Map(pos, z_offset));
}

float IntersectPlanets(vec3 pos, vec3 dir, out vec3 normal, out float max_d, out int type)
{
    const float PLANET_CYCLE = 25.0;
    const int PLANET_PASSES = 3;
    float best_dist = 1e10;
    bool hit = false;
    max_d = -1e10;
    for(int i = 0; i < PLANET_PASSES; i++)
    {
        int tp = i;
        if(tp >= 3) tp-=3;
        float time2 = time + 20.0*float(i);
        float planetRound = floor(time2 / PLANET_CYCLE);
        float planetPos = time2 - planetRound * PLANET_CYCLE;
        float planetAngle = planetRound * 23.1;
        float planetDistance =  (tp==0) ? 20. :
        						((tp==17) ? 13. :
        						13.);
        vec3 sphereCenter = vec3(cos(planetAngle)*planetDistance,sin(planetAngle)*planetDistance,(PLANET_CYCLE- planetPos)*10.);
        vec3 delta = pos - sphereCenter;
        float sphereRadius = 	(tp==0) ? 13. :
        						((tp==1) ? 7. :
        						7.);
    
        float B = dot(dir, delta);
        float C = dot(delta, delta) - sphereRadius * sphereRadius;
        float D = B*B - C;
        
        if(D >= 0.0)
        {
            float t = -B - sqrt(D);
            if(t >= 0. && t < best_dist)
            {
                vec3 spherePos = pos + t * dir;
                normal = normalize(spherePos - sphereCenter);
                best_dist = t;
                type = tp;
                hit = true;
            }
        }
        max_d = max(max_d, D);
    }
    return hit ? best_dist : -1.;
}

float EarthHeight(vec3 pos)
{
    vec2 coord = vec2(acos(pos.y)/(2.0*PI), atan(pos.x, pos.z)/(2.0*PI));
    vec3 te = texture( iChannel2, coord ).rgb + texture( iChannel2, coord*3.0 ).rgb * .3;
    float landLerp = smoothstep( 0.45, 0.5, te.x);
    vec3 albedo = mix( vec3(0.1, 0.2, 0.45), (vec3(0.055, 0.275, 0.0275) + 0.45*te + te*te*0.5*texture( iChannel2, 2.0*coord.xy ).xyz)*0.4, landLerp );
    return length(pos) - albedo.x*.015;
}

vec3 BackgroundInner(vec3 pos, vec3 dir, bool enableSun, out bool sphereHit, out vec3 spherePos, out vec3 sphereNormal, out vec3 reflectivity)
{
    vec3 nebulaPos = dir.yxz;
    float v = Map5(nebulaPos*1.3 + Map5(nebulaPos*0.5)*3.0) + .1;
    v = 0.;
    
    vec3 color = vec3(0.2);
    vec2 uv = vec2(cos(dir.y), atan(dir.x,dir.z));
    
    vec3 a = texture( iChannel1, uv*3.0).rgb;
    
    vec3 b = texture( iChannel1, uv*3.0).rgb;
    
    color *= (a * b) * 4.;
    color += pow(texture(iChannel1, uv*1.0).rgb, vec3(2.0)) * 1.0;
    if(enableSun)
    {
        float sunDot = max(0., dot(dir, sunDir));
        color += (pow(sunDot, 8.0)*.03 + pow(sunDot, 512.0)) * 5. * sunColor;
    }

    sphereHit = false;
    reflectivity = vec3(0);
    float max_d;
    int type;
    float t = IntersectPlanets(pos, dir, sphereNormal, max_d, type);
    if(t >= 0.0)
    {
        spherePos = pos + t * dir;
        vec2 coord = vec2(acos(sphereNormal.y)/(2.0*PI), atan(sphereNormal.x, sphereNormal.z)/(2.0*PI));
        float time_offset = time*.04;
        coord.y += time_offset;

        if(type == 0)
        {
            float offset = texture( iChannel2, coord ).r * .005;
            vec3 lookup = sphereNormal;
            lookup.xy = rotate(lookup.xy, time_offset*2.0*PI);
            float height = Map5(lookup*4.)*.5+0.9;//texture( iChannel2, coord + vec2(offset)).r;
            height = pow(min(height, 1.),8.);
            vec3 fire = (texture( iChannel2, coord*5. + time*.02).rgb +
                         texture( iChannel2, coord*1. + time*.006).rgb

                        ) * vec3(3,1,1) * .5;

            vec3 albedo = texture( iChannel2, coord + vec2(offset)).rgb * .25 -
                          texture( iChannel2, coord*7.0).rgb * .1;
            color = albedo * max(0., dot(sphereNormal, sunDir)) * sunColor + fire * pow(1.0-height,16.);
        }
        else if(type == 2)
        {
            vec3 te = texture( iChannel2, coord ).rgb + texture( iChannel2, coord*3.0 ).rgb * .3;
        
            float offset = 0.0 + texture( iChannel2, coord).x*.003;
            vec3 albedo = (texture( iChannel2, coord*vec2(.4,0)+vec2(offset,0) ).rgb-.5)*.7 + .4;
            albedo += texture( iChannel2, coord*1.0).rgb * .2;
            albedo += texture( iChannel2, coord*16.0).rgb * .075;
            color = albedo * max(0., dot(sphereNormal, sunDir)) * sunColor;
        }
        else if(type == 1)
        {
            vec3 te = texture( iChannel2, coord ).rgb + texture( iChannel2, coord*3.0 ).rgb * .3;

            vec3 bumpedNormal = normalize(vec3(EarthHeight(sphereNormal + EPSILON.yxx), EarthHeight(sphereNormal + EPSILON.xyx), EarthHeight(sphereNormal + EPSILON.xxy)) - EarthHeight(sphereNormal));
            sphereNormal = bumpedNormal;
            float landLerp = smoothstep( 0.45, 0.5, te.x);
            vec3 albedo = mix( vec3(0.1, 0.2, 0.45), (vec3(0.055, 0.275, 0.0275) + 0.45*te + te*te*0.5*texture( iChannel2, 2.0*coord.xy ).xyz)*0.4, landLerp );
            float specPower = mix(2048., 32., landLerp);
            float q = (  texture( iChannel2, coord+vec2(0,time*.02) ).x +
                            texture( iChannel2, coord*2.0+vec2(0,time*.013) ).x) * .5;

            float skyLerp = smoothstep( 0.4, 0.8, q);
            reflectivity = mix(vec3(0.1), vec3(0.0), skyLerp);

            float NdotL = max(0., dot(sphereNormal, sunDir));
            vec3 opaque = albedo * NdotL * sunColor;
            color = opaque + pow(max(0., dot(bumpedNormal, normalize(-dir + sunDir))), specPower) * (specPower + 8.0) / (8.0 * PI) * sunColor * reflectivity;

            vec3 sky = vec3(0.9) * NdotL * sunColor;
            color = mix( color, sky, skyLerp);        
        }

        sphereHit = true;
    }
    
    return color;
}
    
vec3 Background(vec3 pos, vec3 dir)
{
    dir = normalize(dir);
    
    bool sphereHit;
    vec3 spherePos;
    vec3 sphereNormal;
    vec3 reflectivity;
    vec3 color = BackgroundInner(pos, dir, true, sphereHit, spherePos, sphereNormal, reflectivity);
    if(sphereHit)
    {
        vec3 R = Fresnel(reflectivity, sphereNormal, -dir);

        vec3 reflectionDir = reflect(dir,sphereNormal);
        bool dummyHit;
        vec3 dummyPos;
        vec3 dummyNormal;
        color += (BackgroundInner(spherePos + sphereNormal*.01, reflectionDir, false, dummyHit, dummyPos, dummyNormal, reflectivity)*(1.0-R)+vec3(1,2,3)*.075)*R*sunColor;
    }
    
    return color;
}


vec3 LensFlare(vec2 x, in vec2 iResolution)
{
    x = abs(x);
    float e = 1.5;
    float d = pow(pow(x.x*.5, e) + pow(x.y*3., e), 1./e);
    
    vec3 c = vec3(exp(-2.5*d))*sunColor*(.3+sin(x.y*iResolution.y*2.)*.01) * .5;
    c += vec3(exp(-dot(vec3(d),vec3(d))))*sunColor*.05;
    
    return c;
}

void mainImage( out vec4 fragColor, in vec2 fragCoord, in vec2 iResolution )
{
    vec2 uv = fragCoord / iResolution.xy;
    
    vec3 camPos = vec3(0);
    float z_offset = time*SPEED;
 
    float introFade = 1.0;
    
    float camZoom = 1.0;
    
    float tunnelShake = 0.0;
    camPos.xy += TunnelCenter(camPos.z-z_offset)*.5;
    vec3 camTarget = vec3(0,0,5);
    camTarget.xy += TunnelCenter(camTarget.z-z_offset)*.5;
    //camTarget = vec3(3,0,5);
    camTarget = mix(vec3(3,0,5), camTarget, introFade);
    
    float camAngle = 1.0;
    vec3 camUp = vec3(sin(camAngle),cos(camAngle),0);

    mat4 viewToWorld = LookAt(camPos, camTarget, camUp);
    vec2 uv2 = (fragCoord - .5*iResolution.xy) / (iResolution.y * camZoom);
    vec3 eyeDir = (viewToWorld * normalize(vec4(uv2, 1, 0))).xyz;
    
    float t = 0.0;
    vec3 p = camPos;
    float iterationCount = 0.0;
    for(int i = 0; i < 70; i++)
    {
        float dist = Map(p, z_offset);
        
        t += dist;
        p += dist*eyeDir;
        iterationCount++;
        if(abs(dist) < .001) break;
    }
    
    
    vec3 normal = Normal(p, z_offset);
    vec3 refraction = refract(normalize(eyeDir), normal, 1.0);
    vec3 reflection = reflect(-normalize(eyeDir), normal);
    vec3 halfDir = normalize(sunDir - eyeDir);
    vec2 circlePos = p.xy - TunnelCenter(p.z - z_offset);
    float angle = atan(circlePos.y,circlePos.x);
    
    float z = p.z - z_offset;
    
    vec3 R = Fresnel(vec3(0.0), normal, -eyeDir);
    //vec3 c = mix(vec3(2,1,1),vec3(1,1,2),sin(p.z*.1)*.5+.5);
    vec3 c = vec3(1);
    vec3 outColor = Background(p, refraction)*(vec3(1.0) - R)*c + Background(p, -reflection)*R;
    
    float fft = 0.5;
    fft = max(.0, fft - .5);
    
    float tunnelDist = length(p - camPos);
    outColor = outColor * exp(-tunnelDist*.01) + (1.0-exp(-tunnelDist*.05))*vec3(2,1,3)*(.1+fft*0.6);
    
    outColor += sqrt(iterationCount)*.005;
    
    vec3 sunPos = (vec4(sunDir, 0) * viewToWorld).xyz;
    vec2 sunUV = sunPos.xy / sunPos.z;

    float vignette = uv.x * (1.0-uv.x) * uv.y * (1.0-uv.y) * 32. * 0.75 + 0.25;
    outColor *= vignette;
    
    vec3 sphereNormal;
    float max_d;
    int type;
    float planet_t = IntersectPlanets(camPos, sunDir, sphereNormal, max_d, type);
    float lensIntensity = clamp(1.0 - max_d*.02, 0.0, 1.0);
    
    outColor += LensFlare(uv2 - sunUV, iResolution) * lensIntensity;
    outColor += LensFlare(uv2 + sunUV, iResolution) * .4 * lensIntensity;

    outColor = clamp(outColor, 0.0, 1.0);
    //outColor *= vec3(sqrt(min(time*.2, 1.0)));
    fragColor = vec4( outColor, 1.0 );
}

void fragment(){
	vec2 iResolution=1./TEXTURE_PIXEL_SIZE;
	mainImage(COLOR,UV*iResolution,iResolution);
}
Tags
moving, planet, sky, skybox, space
This shader is a port from an existing Shadertoy project. Shadertoy shaders are by default protected under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) license unless anything else has been stated by the author. For more info, see our License terms.

Related shaders

Moving Radial Color Gradient

Moving CheckerBoard

Moving Rainbow Gradient (GD 4.0)

Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments