// Copyright (c) 2023-2025 Cory Petkovsek and Contributors // Copyright (c) 2021 J. Cuellar shader_type spatial; render_mode unshaded, depth_draw_never, cull_front, skip_vertex_transform; // Color correction uniform vec2 _color_correction_params; uniform vec4 _ground_color: source_color; uniform float _horizon_level; // uniforms uniform vec3 _sun_direction; uniform vec3 _moon_direction; uniform float _moon_size; uniform mat3 _moon_matrix; uniform mat3 _deep_space_matrix; // Atmospheric Scattering uniform float _atm_darkness; uniform float _atm_sun_intensity; uniform vec4 _atm_day_tint: source_color; uniform vec4 _atm_horizon_light_tint: source_color; uniform vec4 _atm_night_tint: source_color; uniform vec3 _atm_level_params; uniform float _atm_thickness; // Sun mie phase uniform vec4 _atm_sun_mie_tint: source_color; uniform float _atm_sun_mie_intensity; uniform vec4 _atm_moon_mie_tint: source_color; uniform float _atm_moon_mie_intensity; uniform vec3 _atm_beta_ray; uniform vec3 _atm_beta_mie; uniform vec3 _atm_sun_partial_mie_phase; uniform vec3 _atm_moon_partial_mie_phase; // Sun disk uniform vec4 _sun_disk_color: source_color; uniform float _sun_disk_intensity; uniform float _sun_disk_size; uniform vec4 _moon_color: source_color; uniform sampler2D _moon_texture: source_color, repeat_disable; // Background uniform sampler2D _background_texture: source_color; uniform vec4 _background_color: source_color; // Stars Field uniform vec4 _stars_field_color: source_color; uniform sampler2D _stars_field_texture: source_color; uniform float _stars_scintillation; uniform float _stars_scintillation_speed; uniform sampler2D _noise_tex: source_color; // Clouds uniform float _clouds_coverage; uniform float _clouds_thickness; uniform float _clouds_absorption; uniform float _clouds_sky_tint_fade; uniform float _clouds_intensity; uniform float _clouds_size; uniform vec2 _clouds_uv; uniform float _clouds_speed; uniform vec2 _clouds_direction; uniform vec4 _clouds_day_color: source_color; uniform vec4 _clouds_horizon_light_color: source_color; uniform vec4 _clouds_night_color: source_color; uniform sampler2D _clouds_texture; // Inc //------------------------------------------------------------------------------ const float kPI = 3.1415927f; const float kINV_PI = 0.3183098f; const float kHALF_PI = 1.5707963f; const float kINV_HALF_PI = 0.6366198f; const float kQRT_PI = 0.7853982f; const float kINV_QRT_PI = 1.2732395f; const float kPI4 = 12.5663706f; const float kINV_PI4 = 0.0795775f; const float k3PI16 = 0.1193662f; const float kTAU = 6.2831853f; const float kINV_TAU = 0.1591549f; const float kE = 2.7182818f; float saturate(float value){ return clamp(value, 0.0, 1.0); } vec3 saturateRGB(vec3 value){ return clamp(value.rgb, 0.0, 1.0); } // pow3 vec3 contrastLevel(vec3 vec, float level){ return mix(vec, vec * vec * vec, level); } vec3 tonemapPhoto(vec3 color, float exposure, float level){ color.rgb *= exposure; return mix(color.rgb, 1.0 - exp(-color.rgb), level); } vec2 equirectUV(vec3 norm){ vec2 ret; ret.x = (atan(norm.x, norm.z) + kPI) * kINV_TAU; ret.y = acos(norm.y) * kINV_PI; return ret; } /* float random(vec2 uv){ float ret = dot(uv, vec2(12.9898, 78.233)); return fract(43758.5453 * sin(ret)); } */ float disk(vec3 norm, vec3 coords, lowp float size){ float dist = length(norm - coords); return 1.0 - step(size, dist); } // Atmosphere Inc //------------------------------------------------------------------------------ const float RAYLEIGH_ZENITH_LENGTH = 8.4e3; const float MIE_ZENITH_LENGTH = 1.25e3; float rayleighPhase(float mu){ return k3PI16 * (1.0 + mu * mu); } float miePhase(float mu, vec3 partial){ return kPI4 * (partial.x) * (pow(partial.y - partial.z * mu, -1.5)); } // Simplifield for more performance void simpleOpticalDepth(float y, out float sr, out float sm){ y = max(0.03, y + 0.03) + _atm_level_params.y; y = 1.0 / (y * _atm_level_params.x); sr = y * RAYLEIGH_ZENITH_LENGTH; sm = y * MIE_ZENITH_LENGTH; } // Paper based void opticalDepth(float y, out float sr, out float sm){ y = max(0.0, y); y = saturate(y * _atm_level_params.x); float zenith = acos(y); zenith = cos(zenith) + 0.15 * pow(93.885 - ((zenith * 180.0) / kPI), -1.253); zenith = 1.0 / (zenith + _atm_level_params.y); sr = zenith * RAYLEIGH_ZENITH_LENGTH; sm = zenith * MIE_ZENITH_LENGTH; } vec3 atmosphericScattering(float sr, float sm, vec2 mu, vec3 mult){ vec3 betaMie = _atm_beta_mie; vec3 betaRay = _atm_beta_ray * _atm_thickness; vec3 extcFactor = saturateRGB(exp(-(betaRay * sr + betaMie * sm))); float extcFF = mix(saturate(_atm_thickness * 0.5), 1.0, mult.x); vec3 finalExtcFactor = mix(1.0 - extcFactor, (1.0 - extcFactor) * extcFactor, extcFF); float rayleighPhase = rayleighPhase(mu.x); vec3 BRT = betaRay * rayleighPhase; vec3 BMT = betaMie * miePhase(mu.x, _atm_sun_partial_mie_phase); BMT *= _atm_sun_mie_intensity * _atm_sun_mie_tint.rgb; vec3 BRMT = (BRT + BMT) / (betaRay + betaMie); vec3 scatter = _atm_sun_intensity * (BRMT * finalExtcFactor) * _atm_day_tint.rgb * mult.y; scatter = mix(scatter, scatter * (1.0 - extcFactor), _atm_darkness); vec3 lcol = mix(_atm_day_tint.rgb, _atm_horizon_light_tint.rgb, mult.x); vec3 nscatter = (1.0 - extcFactor) * _atm_night_tint.rgb; nscatter += miePhase(mu.y, _atm_moon_partial_mie_phase) * _atm_moon_mie_tint.rgb * _atm_moon_mie_intensity * 0.005; return (scatter * lcol) + nscatter; } // Clouds //------------------------------------------------------------------------------ float noiseClouds(vec2 coords, vec2 offset){ float speed = TIME * _clouds_speed * .5; // .5 matches cumulus clouds vec2 wind = offset * speed; vec2 wind2 = (offset + offset) * speed; float a = textureLod(_clouds_texture, coords.xy * _clouds_uv - wind, 0.0).r; float b = textureLod(_clouds_texture, coords.xy * _clouds_uv - wind2, 0.0).r; return ((a + b) * 0.5); } float cloudsDensity(vec2 p, vec2 offset){ float d = noiseClouds(p, offset); float c = 1.0 - _clouds_coverage; d = d - c; return saturate(d); } vec4 renderClouds(vec3 pos){ pos.xy = pos.xz / pos.y; pos *= _clouds_size; float density = cloudsDensity(pos.xy, _clouds_direction); float sh = saturate(exp(-_clouds_absorption * density)); float a = saturate(density * _clouds_thickness); return vec4(vec3(density*sh) * _clouds_intensity, a); } // Varyings //------------------------------------------------------------------------------ varying vec4 world_pos; varying vec4 moon_coords; varying vec3 deep_space_coords; varying vec4 angle_mult; varying vec3 scatter; void vertex(){ vec4 vert = vec4(VERTEX, 0.0); POSITION = PROJECTION_MATRIX * MODELVIEW_MATRIX * vert; POSITION.z = 0.00000001; world_pos = MODEL_MATRIX * vert; moon_coords.xyz = (_moon_matrix * VERTEX) / _moon_size + 0.5; moon_coords.w = dot(world_pos.xyz, _moon_direction.xyz); deep_space_coords.xyz = (_deep_space_matrix * VERTEX).xyz; angle_mult.x = saturate(1.0 - _sun_direction.y); angle_mult.y = saturate(_sun_direction.y + 0.45); angle_mult.z = saturate(-_sun_direction.y + 0.30); angle_mult.w = saturate(-_sun_direction.y + 0.60); // Atmosphere vec3 worldPos = normalize(world_pos).xyz; vec2 mu = vec2(dot(_sun_direction, worldPos), dot(_moon_direction, worldPos)); float sr, sm; simpleOpticalDepth(worldPos.y + _atm_level_params.z + _horizon_level, sr, sm); scatter = atmosphericScattering(sr, sm, mu.xy, angle_mult.xyz); } void fragment(){ vec3 col = vec3(0.0); vec3 worldPos = normalize(world_pos).xyz; vec3 cloudsPos = worldPos; worldPos.y += _horizon_level; float horizonBlend = saturate((worldPos.y - 0.03) * 3.0); col.rgb += scatter.rgb; // Near Space vec3 nearSpace = vec3(0.0); vec3 sunDisk = disk(worldPos, _sun_direction, _sun_disk_size) * _sun_disk_color.rgb * scatter.rgb; sunDisk *= _sun_disk_intensity; // Moon vec4 moon = texture(_moon_texture, vec2(-moon_coords.x+1.0, moon_coords.y)); moon.rgb = contrastLevel(moon.rgb * _moon_color.rgb, _moon_color.a); moon *= saturate(moon_coords.w); float moonMask = saturate(1.0 - moon.a); nearSpace = moon.rgb + (sunDisk.rgb * moonMask); col.rgb += nearSpace; vec3 deepSpace = vec3(0.0); vec2 deepSpaceUV = equirectUV(normalize(deep_space_coords)); // Background vec3 deepSpaceBackground = textureLod(_background_texture, deepSpaceUV, 0.0).rgb; deepSpaceBackground *= _background_color.rgb; deepSpaceBackground = contrastLevel(deepSpaceBackground, _background_color.a); deepSpace.rgb += deepSpaceBackground.rgb * moonMask; // Stars Field float starsScintillation = textureLod(_noise_tex, UV + (TIME * _stars_scintillation_speed), 0.0).r; starsScintillation = mix(1.0, starsScintillation * 1.5, _stars_scintillation); vec3 starsField = textureLod(_stars_field_texture, deepSpaceUV, 0.0).rgb * _stars_field_color.rgb; starsField = saturateRGB(mix(starsField.rgb, starsField.rgb * starsScintillation, _stars_scintillation)); //deepSpace.rgb -= saturate(starsField.r*10.0); deepSpace.rgb += starsField.rgb * moonMask; deepSpace.rgb *= angle_mult.z; col.rgb += deepSpace.rgb * horizonBlend; // Clouds vec4 clouds = renderClouds(cloudsPos); clouds.a = saturate(clouds.a); clouds.rgb *= mix(mix(_clouds_day_color.rgb, _clouds_horizon_light_color.rgb, angle_mult.x), _clouds_night_color.rgb, angle_mult.w); clouds.a = mix(0.0, clouds.a, horizonBlend); col.rgb = mix(col.rgb, clouds.rgb + mix(vec3(0.0), scatter, _clouds_sky_tint_fade), clouds.a); col.rgb = mix(col.rgb, _ground_color.rgb * scatter, saturate((-worldPos.y - _atm_level_params.z)*100.0)); col.rgb = tonemapPhoto(col.rgb, _color_correction_params.y, _color_correction_params.x); ALBEDO = col.rgb; }