This commit is contained in:
2026-10-08 17:38:08 +08:00
parent 4765dfaa02
commit 27b9788a04
78 changed files with 24546 additions and 1 deletions
+161
View File
@@ -0,0 +1,161 @@
// Copyright (c) 2023-2025 Cory Petkovsek and Contributors
// Copyright (c) 2021 J. Cuellar
shader_type spatial;
render_mode blend_mix, cull_front, unshaded, fog_disabled;
uniform vec2 color_correction;
uniform float sea_level: hint_range(-2048,2048,.01) = 0.0;
uniform float fog_density = .0001;
uniform float fog_rayleigh_depth = .115;
uniform float fog_mie_depth = 0.;
uniform float fog_falloff = 3.;
uniform float fog_start = 0.;
uniform float fog_end = 1000.;
uniform vec3 sun_direction = vec3(.25, -.25, .25);
uniform vec3 moon_direction = vec3(.45, -.25, .45);
uniform float atm_darkness = .5;
uniform float atm_sun_intensity = 30.;
uniform vec4 atm_day_tint: source_color = vec4(.78, .85, .98, 1.);
uniform vec4 atm_horizon_light_tint: source_color = vec4(.98, .73, .49, 1.);
uniform vec4 atm_night_tint: source_color = vec4(.16, .2, .25, 1.);
uniform vec3 atm_level_params = vec3(1., 0., 0.);
uniform float atm_thickness = .7;
uniform vec3 atm_beta_ray;
uniform vec3 atm_beta_mie;
uniform vec3 atm_sun_partial_mie_phase;
uniform vec4 atm_sun_mie_tint: source_color = vec4(1.);
uniform float atm_sun_mie_intensity = 1.;
uniform vec3 atm_moon_partial_mie_phase;
uniform vec4 atm_moon_mie_tint: source_color = vec4(.13, .18, .29, 1.);
uniform float atm_moon_mie_intensity = .7;
uniform sampler2D DEPTH_TEXTURE : hint_depth_texture, filter_linear_mipmap;
varying vec4 v_angle_mult;
#include "Common.gdshaderinc"
// Atmosphere
vec3 calc_atmospheric_scatter(float sr, float sm, vec2 mu, vec3 mult, float depth) {
vec3 beta_mie = atm_beta_mie;
vec3 beta_ray = atm_beta_ray * atm_thickness;
vec3 extc_factor = clamp_rgb_to_unit(exp(-(beta_ray * sr + beta_mie * sm)));
float extc_ff = mix(clamp_to_unit(atm_thickness * 0.5), 1.0, mult.x);
vec3 final_extc_factor = mix(1.0 - extc_factor, (1.0 - extc_factor) * extc_factor, extc_ff);
float rayleigh_phase = calc_rayleigh_phase(mu.x);
vec3 BRT = beta_ray * rayleigh_phase * clamp_to_unit(depth * fog_rayleigh_depth);
vec3 BMT = beta_mie * calc_mie_phase(mu.x, atm_sun_partial_mie_phase);
BMT *= atm_sun_mie_intensity * atm_sun_mie_tint.rgb * clamp_to_unit(depth * fog_mie_depth);
vec3 BRMT = (BRT + BMT) / (beta_ray + beta_mie);
vec3 scatter = atm_sun_intensity * (BRMT * final_extc_factor) * atm_day_tint.rgb * mult.y;
scatter = mix(scatter, scatter * (1.0 - extc_factor), atm_darkness);
vec3 lcol = mix(atm_day_tint.rgb, atm_horizon_light_tint.rgb, mult.x);
vec3 nscatter = (1.0 - extc_factor) * atm_night_tint.rgb * clamp_to_unit(depth * fog_rayleigh_depth);
nscatter += calc_mie_phase(mu.y, atm_moon_partial_mie_phase) *
atm_moon_mie_tint.rgb * atm_moon_mie_intensity * 0.005 * clamp_to_unit(depth * fog_mie_depth);
return (scatter * lcol) + nscatter;
}
// Fog
float calc_fog_exp(float depth, float density) {
return 1.0 - clamp_to_unit(exp2(-depth * density));
}
float calc_fog_falloff(float y, float zeroLevel, float falloff) {
return clamp_to_unit(exp(-(y + zeroLevel) * falloff));
}
float calc_fog_distance(float depth) {
float d = depth;
d = (fog_end - d) / (fog_end - fog_start);
return clamp_to_unit(1.0 - d);
}
// Main
void vertex() {
POSITION = vec4(VERTEX.xy, 1.0, 1.0);
v_angle_mult = vec4(
clamp_to_unit(1.0 - sun_direction.y),
clamp_to_unit(sun_direction.y + 0.45),
clamp_to_unit(-sun_direction.y + 0.30),
clamp_to_unit(-sun_direction.y + 0.60));
}
void fragment() {
// Calculate world pixel position from depth
float depth_raw = texture(DEPTH_TEXTURE, SCREEN_UV).r;
#ifndef CURRENT_RENDERER
// For 4.3 or earlier, will break if far plane is less than 1m.
depth_raw = PROJECTION_MATRIX[3][2] > PROJECTION_MATRIX[2][2] ? depth_raw : depth_raw * 2.0 - 1.0;
#else
#if CURRENT_RENDERER == RENDERER_COMPATIBILITY
depth_raw = depth_raw * 2.0 - 1.0;
#endif
#endif
vec3 ndc = vec3(SCREEN_UV * 2.0 - 1.0, depth_raw);
vec4 clip_pos = INV_PROJECTION_MATRIX * vec4(ndc, 1.0);
vec3 view_pos = clip_pos.xyz / clip_pos.w;
vec4 world_pos_hom = INV_VIEW_MATRIX * vec4(view_pos, 1.0);
vec3 world_position = world_pos_hom.xyz / world_pos_hom.w;
// Ray from camera to world intersection point
vec3 intersection_ray = world_position - CAMERA_POSITION_WORLD;
// Unit ray direction
vec3 ray_direction = normalize(intersection_ray);
//float linear_depth = -view_pos.z; // Approximate ray distance
//float ray_distance = length(intersection_ray); // Exact ray distance
// Cap ray if there's a sea_level plane intersection
float original_distance = length(intersection_ray);
// Y difference from camera to sea_level
float dy = sea_level - CAMERA_POSITION_WORLD.y;
// Distance along ray to sea_level plane
float plane_distance = dy / (ray_direction.y + 1e-6 * sign(ray_direction.y)); // avoid divide by zero
float plane_hit = float(ray_direction.y < 0.) *
float(plane_distance > 0.) *
float(plane_distance < original_distance);
// Use final distance or t_plane distance Final distance: mix original vs plane distance
float ray_distance = mix(original_distance, plane_distance, plane_hit);
// Capped world position
//world_position = CAMERA_POSITION_WORLD + ray_distance * ray_direction;
float fog_factor = calc_fog_exp(ray_distance, fog_density);
fog_factor *= calc_fog_falloff(ray_direction.y, 0.0, fog_falloff);
fog_factor *= calc_fog_distance(ray_distance);
vec2 mu = vec2(
dot(sun_direction, ray_direction),
dot(moon_direction, ray_direction)
);
float sr, sm;
calc_simple_optical_depth(ray_direction.y + atm_level_params.z, sr, sm, atm_level_params.xy);
vec3 scatter = calc_atmospheric_scatter(sr, sm, mu, v_angle_mult.xyz, ray_distance);
vec4 fog_color = vec4(scatter, fog_factor);
fog_color.a = clamp(fog_color.a, 0.0, 1.0);
fog_color.rgb = apply_photo_tonemap(fog_color.rgb, color_correction.y, color_correction.x);
ALBEDO = fog_color.rgb;
ALPHA = fog_color.a;
// View depth map
//ALPHA = 1.;
//ALBEDO = vec3(ray_distance*.0001);
}
@@ -0,0 +1 @@
uid://bqaetpq827vg6
+64
View File
@@ -0,0 +1,64 @@
// Copyright (c) 2023-2025 Cory Petkovsek and Contributors
// Copyright (c) 2021 J. Cuellar
const float INV_PI = 1. / PI;
const float HALF_PI = PI / 2.;
const float INV_HALF_PI = 1. / HALF_PI;
const float QRT_PI = PI / 4.;
const float INV_QRT_PI = 1. / QRT_PI;
const float PI4 = PI * 4.;
const float INV_PI4 = 1. / PI4;
const float INV_TAU = 1. / TAU;
const float RAYLEIGH_ZENITH_LENGTH = 8.4e3;
const float MIE_ZENITH_LENGTH = 1.25e3;
const float THREE_OVER_EIGHT_PI = 3.0 / (8.0 * PI);
float clamp_to_unit(float value) {
return clamp(value, 0.0, 1.0);
}
vec3 clamp_rgb_to_unit(vec3 value) {
return clamp(value.rgb, 0.0, 1.0);
}
vec3 adjust_contrast(vec3 vec, float level) {
return mix(vec, vec * vec * vec, level); // pow3
}
vec3 apply_photo_tonemap(vec3 color, float exposure, float level) {
color.rgb *= exposure;
return mix(color.rgb, 1.0 - exp(-color.rgb), level);
}
float calc_rayleigh_phase(float mu) {
return THREE_OVER_EIGHT_PI * (1.0 + mu * mu);
}
float calc_mie_phase(float mu, vec3 partial) {
float denominator = partial.y - partial.z * mu;
float safe_denominator = max(denominator, 0.0001); // Prevent division by zero on negative powers
return PI4 * partial.x * pow(safe_denominator, -1.5);
}
// Simplifield for more performance
void calc_simple_optical_depth(float y, out float sr, out float sm, vec2 params) {
y = max(0.03, y + 0.03) + params.y;
y = 1.0 / (y * params.x);
sr = y * RAYLEIGH_ZENITH_LENGTH;
sm = y * MIE_ZENITH_LENGTH;
}
// Paper based (Unused)
void calc_optical_depth(float y, out float sr, out float sm, vec2 params) {
y = max(0.0, y);
y = clamp_to_unit(y * params.x);
float zenith = acos(y);
zenith = cos(zenith) + 0.15 * pow(93.885 - ((zenith * 180.0) * INV_PI), -1.253);
zenith = 1.0 / (zenith + params.y);
sr = zenith * RAYLEIGH_ZENITH_LENGTH;
sm = zenith * MIE_ZENITH_LENGTH;
}
@@ -0,0 +1 @@
uid://0auvnvj85575
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 J. Cuéllar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+517
View File
@@ -0,0 +1,517 @@
// Copyright (c) 2023-2025 Cory Petkovsek and Contributors
// Copyright (c) 2021 J. Cuellar
shader_type sky;
render_mode use_debanding;
group_uniforms general;
uniform bool sky_visible = true;
uniform vec2 color_correction = vec2(0.0, 1.0);
uniform vec4 ground_color: source_color = vec4(0.3, 0.3, 0.3, 1.0);
uniform float horizon_offset = 0.0;
group_uniforms atmosphere;
uniform float atm_darkness = 0.5;
uniform float atm_sun_intensity = 18.0;
uniform vec4 atm_day_tint: source_color = vec4(0.808, 0.91, 1.0, 1.0);
uniform vec4 atm_horizon_light_tint: source_color = vec4(0.98, 0.635, 0.463, 1.0);
uniform vec4 atm_night_tint: source_color = vec4 (0.041, 0.049, 0.061, 0.245);
uniform vec3 atm_level_params = vec3(1.0, 0.0, 0.0);
uniform float atm_thickness = 0.7;
uniform vec3 atm_beta_ray = vec3(0.00001, 0.00001, 0.00003);
uniform vec3 atm_beta_mie = vec3(0.00000003038); // Value computed by multiplying Mie, turbidity, and 0.000434.
group_uniforms sun_disk;
uniform vec4 sun_light_color: source_color = vec4(1.0);
uniform vec4 sun_disk_color: source_color = vec4(0.996, 0.541, 0.14, 1.0);
uniform float sun_disk_intensity = 2.0;
uniform float sun_disk_size = 0.015;
group_uniforms sun_mie;
uniform vec4 atm_sun_mie_tint: source_color = vec4(1.0);
uniform float atm_sun_mie_intensity = 1.0;
uniform vec3 atm_sun_partial_mie_phase = vec3(0.36, 1.64, 1.6);
group_uniforms moon;
uniform vec4 moon_color: source_color = vec4(1.0);
uniform sampler2D moon_texture: source_color, repeat_disable;
uniform vec3 moon_texture_alignment = vec3(7.0, 1.4, 4.8);
uniform bool moon_texture_flip_u;
uniform bool moon_texture_flip_v;
uniform float moon_size = 0.07;
group_uniforms moon_mie;
uniform vec4 atm_moon_mie_tint: source_color = vec4(0.137, 0.184, 0.292, 1.0);
uniform float atm_moon_mie_intensity = 0.17152;
uniform vec3 atm_moon_partial_mie_phase = vec3(0.36, 1.64, 1.6);
group_uniforms star_map;
uniform vec4 starmap_color: source_color = vec4(0.71, 0.71, 0.71, 0.855);
uniform sampler2D starmap_texture: source_color;
uniform bool starmap_flip_u = false;
uniform bool starmap_flip_v = false;
uniform vec3 starmap_alignment = vec3(2.68288, -0.25891, 0.40101);
uniform float star_rotation_offset = 9.38899;
uniform float star_rotation = 0.0; // For simulating Earth rotation.
uniform float star_tilt = 0.0; // For tilting the sky according to observer latitude in radians. (90° - latitude)
group_uniforms star_field;
uniform vec4 star_field_color: source_color = vec4(1.0);
uniform sampler2D star_field_texture: source_color;
uniform float star_scintillation = 0.75;
uniform float star_scintillation_speed = 0.01;
uniform sampler2D noise_tex: source_color;
/* Sky alignment values for a star map that is in galactic coordinates. Works for most star maps.
X: 2.6555
Y: -0.23935
Z: 0.4505
These alignment values are consistent with a sky view from the North Pole (latitude zero)
at midnight. The positions of Polaris, Vega, Rigel, and Alpha Centauri confirmed
through various sources and calculations.
*/
group_uniforms clouds;
uniform vec4 clouds_night_color: source_color = vec4(0.09, 0.094, 0.129, 1.0);
group_uniforms cirrus_clouds;
uniform bool cirrus_visible = true;
uniform float cirrus_intensity = 2.0;
uniform float cirrus_coverage = 0.5;
uniform float cirrus_thickness = 10.0;
uniform float cirrus_absorption = 2.0;
uniform float cirrus_sky_tint_fade = 0.5;
uniform float cirrus_size = 1.0;
uniform vec2 cirrus_uv = vec2(0.16, 0.11);
uniform vec2 cirrus_position1 = vec2(0.0);
uniform vec2 cirrus_position2 = vec2(0.0);
uniform sampler2D cirrus_texture;
group_uniforms cumulus_clouds;
uniform bool cumulus_visible = true;
uniform float cumulus_intensity = 0.6;
uniform float cumulus_coverage = 0.55;
uniform float cumulus_thickness = 0.0243;
uniform float cumulus_absorption = 2.0;
uniform float cumulus_noise_freq = 2.7;
uniform float cumulus_sky_tint_fade = 0.0;
uniform float cumulus_size = 0.5;
uniform vec2 cumulus_position = vec2(0.0);
uniform sampler2D cumulus_texture;
uniform vec3 cumulus_partial_mie_phase = vec3(0.95756, 1.04244, 0.412);
uniform float cumulus_mie_intensity = 1.0;
group_uniforms overlays;
uniform bool show_azimuthal_grid = false;
uniform vec4 azimuthal_grid_color : source_color = vec4(.871, .722, .529, 1.);
uniform float azimuthal_grid_rotation_offset = 0.03;
uniform bool show_equatorial_grid = false;
uniform vec4 equatorial_grid_color : source_color = vec4(0., .75, 1., 1.);
uniform float equatorial_grid_rotation_offset = 0.03;
group_uniforms transform_matrices;
uniform mat3 moon_matrix; // Calculated externally via GDScript.
#include "Common.gdshaderinc"
// Provides a transformation matrix when passed a vec3.
mat3 create_rotation_matrix(vec3 r) {
float c1 = cos(r.x), s1 = sin(r.x);
float c2 = cos(r.y), s2 = sin(r.y);
float c3 = cos(r.z), s3 = sin(r.z);
mat3 rotX = mat3(
vec3(1.0, 0.0, 0.0),
vec3(0.0, c1, -s1),
vec3(0.0, s1, c1)
);
mat3 rotY = mat3(
vec3(c2, 0.0, s2),
vec3(0.0, 1.0, 0.0),
vec3(-s2, 0.0, c2)
);
mat3 rotZ = mat3(
vec3(c3, -s3, 0.0),
vec3(s3, c3, 0.0),
vec3(0.0, 0.0, 1.0)
);
return rotZ * rotY * rotX; // Combine rotations in ZYX order
}
vec2 calc_equirect_uv(vec3 norm) {
float flipu = starmap_flip_u ? -1.0 : 1.0;
float flipv = starmap_flip_v ? -1.0 : 1.0;
float u = flipu * ((atan(norm.y, norm.x) + PI) * INV_TAU); // 0 to 1 horizontally
float v = -flipv * (acos(norm.z) * INV_PI); // 0 (north pole) to 1 (south pole)
return vec2(u, v);
}
vec3 sample_moon_texture(vec3 dir) {
dir = moon_matrix * dir * create_rotation_matrix(moon_texture_alignment);
dir.x = -dir.x;
dir.z = -dir.z;
float flipu = moon_texture_flip_u ? 1.0 : -1.0;
float flipv = moon_texture_flip_v ? 1.0 : -1.0;
float u = 0.5 + flipu * atan(dir.z, dir.x) * INV_TAU; // Longitude
float v = 0.5 + flipv * asin(dir.y) * INV_PI; // Latitude
return texture(moon_texture, vec2(u, v)).rgb;
}
float calc_disk_mask(vec3 norm, vec3 coords, lowp float size) {
float dist = length(norm - coords);
return 1.0 - step(size, dist);
}
float map_range(float value, float from_min, float from_max, float to_min, float to_max) {
return to_min + (value - from_min) * (to_max - to_min) / (from_max - from_min);
}
bool ray_sphere_intersect(float r, vec3 origin, vec3 dir, out float t, out vec3 nrm) {
origin += vec3(0.0, 450.0, 0.0);
float a = dot(dir, dir);
float b = 2.0 * dot(origin, dir);
float c = dot(origin, origin) - r * r;
float d = b * b - 4.0 * a * c;
float valid = step(0.0, d); // 1.0 if d >= 0.0, else 0.0
d = sqrt(max(d, 0.0)); // Prevent sqrt of negative numbers
a *= 2.0;
float t1 = 0.5 * (-b + d);
float t2 = 0.5 * (-b - d);
float t_min = min(t1, t2); // Select the smallest intersection
t = mix(t2, t1, step(t2, t1) * step(0.0, t1)); // Use t1 if t2 < t1 and t1 >= 0, else t2
t = mix(0.0, t, step(0.0, t) * valid); // Invalidate if t < 0 or d < 0
nrm = mix(vec3(0.0), origin + t * dir, step(0.0, t) * valid); // Set nrm only if valid
return bool(step(0.0, t) * valid);
}
// For rendering the moon. This function courtesy of Kelvin van Hoorn.
// https://kelvinvanhoorn.com/2024/12/07/skybox-tutorial-godot/
float simple_sphere_intersect(vec3 ray_dir, vec3 sphere_pos, float radius) {
vec3 oc = -sphere_pos;
float b = dot(oc, ray_dir);
float c = dot(oc, oc) - radius * radius;
float h = b * b - c;
float valid = step(0.0, h); // 1.0 if h >= 0.0, else 0.0
h = sqrt(max(h, 0.0)); // Ensure sqrt is non-negative
return mix(-1.0, -b - h, valid);
}
// Atmosphere
//------------------------------------------------------------------------------
vec3 calc_atmospheric_scatter(float sr, float sm, vec2 mu, vec3 mult) {
vec3 beta_mie = atm_beta_mie;
vec3 beta_ray = atm_beta_ray * atm_thickness;
vec3 extc_factor = clamp_rgb_to_unit(exp(-(beta_ray * sr + beta_mie * sm)));
float extc_ff = mix(clamp_to_unit(atm_thickness * 0.5), 1.0, mult.x);
vec3 final_extc_factor = mix(1.0 - extc_factor, (1.0 - extc_factor) * extc_factor, extc_ff);
float rayleigh_phase = calc_rayleigh_phase(mu.x);
vec3 BRT = beta_ray * rayleigh_phase;
vec3 BMT = beta_mie * calc_mie_phase(mu.x, atm_sun_partial_mie_phase);
BMT *= atm_sun_mie_intensity * atm_sun_mie_tint.rgb;
vec3 BRMT = (BRT + BMT) / (beta_ray + beta_mie);
vec3 scatter = atm_sun_intensity * (BRMT * final_extc_factor) * atm_day_tint.rgb * mult.y;
scatter = mix(scatter, scatter * (1.0 - extc_factor), atm_darkness);
vec3 lcol = mix(atm_day_tint.rgb, atm_horizon_light_tint.rgb, mult.x);
vec3 nscatter = (1.0 - extc_factor) * atm_night_tint.rgb;
nscatter += calc_mie_phase(mu.y, atm_moon_partial_mie_phase) *
atm_moon_mie_tint.rgb * atm_moon_mie_intensity * 0.005;
return (scatter * lcol) + nscatter;
}
// Clouds
//------------------------------------------------------------------------------
const int CUMULUS_STEP = 10;
float sample_cirrus_noise(vec2 coords) {
float a = textureLod(cirrus_texture, coords.xy * cirrus_uv + cirrus_position1, 0.0).r;
float b = textureLod(cirrus_texture, coords.xy * cirrus_uv + cirrus_position2, 0.0).r;
return ((a + b) * 0.5);
}
float sample_cumulus_noise(vec3 p) {
vec3 pos = vec3(p * 0.01);
pos.z *= 256.0;
vec2 offset = vec2(0.317, 0.123);
vec4 uv = vec4(0.0);
uv.xy = pos.xy + offset * floor(pos.z);
uv.zw = uv.xy + offset;
float x1 = textureLod(cumulus_texture, uv.xy, 0.0).r;
float x2 = textureLod(cumulus_texture, uv.zw, 0.0).r;
return mix(x1, x2, fract(pos.z));
}
float calc_cumulus_fbm(vec3 p, float l) {
float ret;
ret = 0.51749673 * sample_cumulus_noise(p);
p *= l;
ret += 0.25584929 * sample_cumulus_noise(p);
p *= l;
ret += 0.12527603 * sample_cumulus_noise(p);
p *= l;
ret += 0.06255931 * sample_cumulus_noise(p);
return ret;
}
float calc_cirrus_density(vec2 p) {
float d = sample_cirrus_noise(p);
float c = 1.0 - cirrus_coverage;
d = d - c;
return clamp_to_unit(d);
}
float calc_cumulus_density(vec3 p, vec3 offset, float t) {
vec3 pos = p * 0.0212242 - offset;
float dens = calc_cumulus_fbm(pos, cumulus_noise_freq);
dens += dens;
float cov = 1.0 - cumulus_coverage;
cov = smoothstep(0.00, (cov * 3.5) + t, dens);
dens *= cov;
dens = map_range(dens, 1.0 - cov, 1.0, 0.0, 1.0);
return clamp_to_unit(dens);
}
vec4 render_cirrus_clouds(vec3 pos) {
pos.xy = pos.xz / pos.y;
pos *= cirrus_size;
float density = calc_cirrus_density(pos.xy);
float sh = clamp_to_unit(exp(-cirrus_absorption * density));
float a = clamp_to_unit(density * cirrus_thickness);
return vec4(vec3(density*sh) * cirrus_intensity, a);
}
vec4 render_cumulus_clouds(vec3 ro, vec3 rd, float am, vec3 sun_pos, vec3 moon_pos) {
vec4 ret = vec4(0.);
vec3 wind = -vec3(cumulus_position.x, 0.0, cumulus_position.y);
float a = 0.0;
// n and tt doesnt need to be initialized since it would be set by ray_sphere_intersect
vec3 n; float tt;
if (ray_sphere_intersect(500., ro, rd, tt, n)) {
float march_step = float(CUMULUS_STEP) * cumulus_thickness;
vec3 dir_step = rd / rd.y * march_step;
vec3 pos = n * cumulus_size;
vec2 mu = vec2(dot(sun_pos, rd), dot(moon_pos, rd));
vec3 mph = ((calc_mie_phase(mu.x, cumulus_partial_mie_phase) * atm_sun_mie_tint.rgb) +
calc_mie_phase(mu.y, cumulus_partial_mie_phase) * am);
vec4 t = vec4(1.0);
t.rgb += (mph.rgb * cumulus_mie_intensity);
for(int i = 0; i < CUMULUS_STEP; i++) {
float h = float(i) * 0.1; // / float(kCLOUDS_STEP);
float density = calc_cumulus_density(pos, wind, h);
float sh = clamp_to_unit(exp(-cumulus_absorption * density * march_step));
t *= sh;
ret += (t * (exp(h) * 0.571428571) * density * march_step);
a += (1.0 - sh) * (1.0 - a);
pos += dir_step;
}
return vec4(ret.rgb * cumulus_intensity, a);
}
return vec4(ret.rgb * cumulus_intensity, a);
}
// Main
//------------------------------------------------------------------------------
vec3 render_sky(vec3 world_pos, vec3 clouds_pos, vec3 sun_pos, vec3 moon_pos, float p_time) {
vec3 col = vec3(0.0);
vec4 angle_mult;
angle_mult.x = clamp_to_unit(1.0 - sun_pos.y);
angle_mult.y = clamp_to_unit(sun_pos.y + 0.45);
angle_mult.z = clamp_to_unit(-sun_pos.y + 0.30);
angle_mult.w = clamp_to_unit(-sun_pos.y + 0.60);
// Atmosphere
vec2 mu = vec2(dot(sun_pos, world_pos), dot(moon_pos, world_pos));
float sr, sm;
calc_simple_optical_depth(world_pos.y + atm_level_params.z - horizon_offset, sr, sm, atm_level_params.xy);
world_pos.y -= horizon_offset;
float horizonBlend = clamp_to_unit((world_pos.y - 0.03) * 3.0);
float horizonBlendCumulus = clamp_to_unit((world_pos.y + 0.01) * 50.0);
vec3 scatter = calc_atmospheric_scatter(sr, sm, mu.xy, angle_mult.xyz);
col.rgb += scatter.rgb;
// Sun
vec3 sun_disk = calc_disk_mask(world_pos, sun_pos, sun_disk_size) * sun_disk_color.rgb * scatter.rgb;
sun_disk *= sun_disk_intensity;
// Moon
float moon_intersect = simple_sphere_intersect(world_pos, moon_pos, moon_size);
float moon_mask = moon_intersect > (-1. + sqrt(moon_size)) ? 1.0 : 0.0;
vec3 moon_normal = normalize(world_pos * moon_intersect - moon_pos);
float moon_ndotl = clamp(dot(moon_normal, sun_pos), 0.0, 1.0);
vec3 moon_tex = sample_moon_texture(moon_normal);
vec3 moon_output = moon_mask * moon_ndotl * exp2(1.0) * moon_tex * moon_color.rgb;
float moonMask = (1.0 - moon_mask);
// Deep space
// Tilt the sky along the X-axis.
mat3 tilt = create_rotation_matrix(vec3(star_tilt, 0.0, 0.0));
// Rotation around Y-axis. For simulating Earth's rotation.
mat3 rotation = create_rotation_matrix(vec3(0.0, star_rotation + star_rotation_offset, 0.0));
// Initial XYZ rotation for alignment.
mat3 alignment_xyz = create_rotation_matrix(starmap_alignment);
// Combine everything together to get the desired alignment.
vec3 final_alignment = normalize(alignment_xyz * rotation * tilt * world_pos);
// Now generate UV coordinates for the texture.
vec2 deep_space_uv = calc_equirect_uv(normalize(final_alignment));
// Star map
vec3 deep_space_background = textureLod(starmap_texture, deep_space_uv, 0.0).rgb;
deep_space_background *= starmap_color.rgb;
deep_space_background = adjust_contrast(deep_space_background, starmap_color.a);
vec3 deep_space = deep_space_background.rgb * moonMask + moon_output;
// Star Field
float star_scint = textureLod(noise_tex, deep_space_uv + (p_time * star_scintillation_speed), 0.0).r;
star_scint = mix(1.0, star_scint * 1.5, star_scintillation);
vec3 star_field = textureLod(star_field_texture, deep_space_uv, 0.0).rgb * star_field_color.rgb;
star_field = clamp_rgb_to_unit(mix(star_field.rgb, star_field.rgb * star_scint, star_scintillation));
star_field = clamp_rgb_to_unit(star_field.rgb);
//deep_space.rgb -= clamp_to_unit(star_field.r * 10.0);
deep_space.rgb += star_field.rgb * moonMask;
deep_space.rgb *= angle_mult.z;
col.rgb += sun_disk.rgb + deep_space.rgb * horizonBlend;
// Clouds
if (cirrus_visible) {
vec4 cirrus_clouds = render_cirrus_clouds(clouds_pos);
cirrus_clouds.a = clamp_to_unit(cirrus_clouds.a);
vec3 cloud_tint = sun_light_color.rgb;
cloud_tint = mix(cloud_tint, clouds_night_color.rgb, angle_mult.w);
cirrus_clouds.rgb *= cloud_tint * cirrus_intensity;
cirrus_clouds.a = mix(0.0, cirrus_clouds.a, horizonBlend);
col.rgb = mix(col.rgb, cirrus_clouds.rgb + mix(vec3(0.0), scatter, cirrus_sky_tint_fade), cirrus_clouds.a);
}
if (cumulus_visible) {
vec4 cumulus_clouds = render_cumulus_clouds(vec3(0.0), clouds_pos, angle_mult.z, sun_pos, moon_pos);
cumulus_clouds.a = clamp_to_unit(cumulus_clouds.a);
vec3 cloud_tint = sun_light_color.rgb;
cloud_tint = mix(cloud_tint, clouds_night_color.rgb, angle_mult.w);
cumulus_clouds.rgb *= cloud_tint * cumulus_intensity;
cumulus_clouds.a = mix(0.0, cumulus_clouds.a, horizonBlend);
col.rgb = mix(col.rgb, cumulus_clouds.rgb + mix(vec3(0.0), scatter, cumulus_sky_tint_fade), cumulus_clouds.a);
}
col.rgb = mix(col.rgb, ground_color.rgb * scatter, clamp_to_unit((-world_pos.y - atm_level_params.z) * 100.0));
col.rgb = apply_photo_tonemap(col.rgb, color_correction.y, color_correction.x);
return col;
}
vec4 create_axis_angle_quat(vec3 axis, float angle) {
float s = sin(angle * 0.5);
return vec4(axis * s, cos(angle * 0.5));
}
vec3 rotate_vector_by_quat(vec3 v, vec4 q) {
vec3 t = 2.0 * cross(q.xyz, v);
return v + q.w * t + cross(q.xyz, t);
}
vec3 align_and_rotate_vector(vec3 dir, vec3 target, float rotation_angle) {
vec3 z_axis = vec3(0.0, 0.0, 1.0); // Original grid alignment
// First, align Z-axis with target (Polaris)
target = normalize(target);
vec3 axis = cross(z_axis, target);
float angle = acos(dot(z_axis, target));
float valid = step(0.0001, length(axis)); // 1.0 if length(axis) >= 0.0001
axis = normalize(axis);
vec4 align_quat = create_axis_angle_quat(axis, angle);
dir = mix(dir, rotate_vector_by_quat(dir, align_quat), valid);
vec4 rot_quat = create_axis_angle_quat(vec3(0.0, 0.0, 1.0), rotation_angle);
dir = mix(dir, rotate_vector_by_quat(dir, rot_quat), valid);
return dir;
}
void render_grid(vec3 dir, vec3 pole_dir, float rotation_angle, vec4 line_color, float grid_spacing, float line_thickness, inout vec3 color) {
float spacing = max(grid_spacing, 0.001);
float thickness = max(line_thickness, 0.0001);
// Rotate dir with sidereal motion and align with pole_dir
dir = align_and_rotate_vector(dir, pole_dir, rotation_angle);
// Latitude / Circles / Theta Spherical coordinates
float theta = acos(dir.z);
float theta_deriv = fwidth(theta);
float circle_thickness = max(thickness, theta_deriv);
float circle = mod(theta, spacing);
float circle_line = 1.0 - smoothstep(0.0, circle_thickness * 1.5, abs(circle - circle_thickness * 0.5));
circle_line *= clamp_to_unit(thickness / circle_thickness);
// Add poles
float pole_thickness = circle_thickness;
float north_pole = 1.0 - smoothstep(0.0, pole_thickness, theta);
float south_pole = 1.0 - smoothstep(0.0, pole_thickness, abs(theta - PI));
circle_line = max(circle_line, max(north_pole, south_pole));
// Darken Lat at poles
float circle_adjust = sin(theta);
circle_line *= clamp(circle_adjust, 0.2, 1.0);
// Longitude / Radial lines / Phi Spherical coordinates
float phi = atan(dir.y, dir.x);
float phi_deriv = fwidth(phi);
float radial_thickness = max(thickness, phi_deriv);
float radial = mod(phi + PI, spacing); // PI w/ less precision to remove moire
float radial_line = 1.0 - smoothstep(0.0, radial_thickness * 1.5, abs(radial - radial_thickness));
radial_line *= clamp_to_unit(thickness / radial_thickness);
float grid = max(radial_line, circle_line);
color += line_color.rgb * line_color.a * grid;
}
void sky() {
float current_time = TIME;
vec3 col = vec3(0.0); // Set up the initial sky color which will be added to later to produce the final result.
vec3 world_pos = EYEDIR; // Original shader set this up in the vertex() function. For sky shaders the overall equivalent is EYEDIR.
vec3 clouds_pos = EYEDIR; // The same is used for cloud rendering.
vec3 sun_pos = LIGHT0_DIRECTION; // Redefined from now-deprecated _sun_direction.
vec3 moon_pos = LIGHT1_DIRECTION; // Redefined from now-deprecated _moon_direction.
if (sky_visible) {
col = render_sky(world_pos, clouds_pos, sun_pos, moon_pos, current_time);
}
// Draw overlay grids
{
float grid_thickness = 0.0002;
float grid_spacing = 0.261799; // 15 degrees
vec3 zenith_dir = vec3(0.0, 1.0, 0.0); // Y-axis aligned azimuthal
vec3 north_celestial_pole = create_rotation_matrix(vec3(star_tilt, 0.0, 0.0)) * zenith_dir;
float rotation_angle = star_rotation + star_rotation_offset;
if (show_azimuthal_grid) {
render_grid(EYEDIR, zenith_dir, azimuthal_grid_rotation_offset, azimuthal_grid_color, grid_spacing, grid_thickness, col);
}
if (show_equatorial_grid) {
render_grid(EYEDIR, north_celestial_pole, rotation_angle + equatorial_grid_rotation_offset, equatorial_grid_color, grid_spacing, grid_thickness, col);
}
}
COLOR = col.rgb;
}
@@ -0,0 +1 @@
uid://cvd0x7lon1ur6