// Ground shader with analytical cloud shadows
// Projects fragment position toward sun to find cloud intersection,
// then samples noise at that position to darken the ground.
shader_type spatial;
uniform vec3 ground_color : source_color = vec3(0.3, 0.35, 0.2);
uniform sampler3D perlworlnoise : filter_linear_mipmap, repeat_enable;
uniform sampler2D weather_map : filter_linear_mipmap, repeat_enable;
uniform float cloud_coverage : hint_range(0.0, 1.0) = 0.35;
uniform float cloud_altitude : hint_range(100.0, 5000.0) = 1500.0;
uniform float shadow_strength : hint_range(0.0, 1.0) = 0.5;
uniform float cloud_time_scale : hint_range(0.0, 20.0) = 2.0;
uniform vec2 cloud_direction = vec2(1.0, 0.0);
float remap(float v, float lo, float hi, float nlo, float nhi) {
return nlo + (v - lo) / (hi - lo) * (nhi - nlo);
}
float cloud_shadow(vec3 world_pos, vec3 light_dir) {
// Project world position toward sun to cloud altitude
if (light_dir.y <= 0.001) return 0.0; // sun below horizon
float t = (cloud_altitude - world_pos.y) / light_dir.y;
vec3 cloud_pos = world_pos + light_dir * t;
// Wind offset
float wind_t = 0.003 * TIME * cloud_time_scale;
vec2 weather_offset = vec2(wind_t * cloud_direction.x, wind_t * cloud_direction.y);
// Sample weather map for coverage
float weather_scale = 0.00006;
float weather_b = texture(weather_map, cloud_pos.xz * weather_scale + 0.5 + weather_offset).b;
// Sample base noise
float noise_scale = 0.00008;
cloud_pos.x += 10.0 * TIME * cloud_time_scale;
vec4 n = textureLod(perlworlnoise, cloud_pos * noise_scale, 0.0);
float fbm = n.g * 0.625 + n.b * 0.25 + n.a * 0.125;
float base = remap(n.r, -(1.0 - fbm), 1.0, 0.0, 1.0);
float cov = cloud_coverage * weather_b;
base = remap(base, 1.0 - cov, 1.0, 0.0, 1.0) * cov;
return clamp(base, 0.0, 1.0) * shadow_strength;
}
varying vec3 world_position;
void vertex() {
world_position = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
}
void fragment() {
ALBEDO = ground_color;
}
void light() {
vec3 light_world = normalize((INV_VIEW_MATRIX * vec4(LIGHT, 0.0)).xyz);
float shadow = cloud_shadow(world_position, light_world);
DIFFUSE_LIGHT += ALBEDO * ATTENUATION * LIGHT_COLOR * max(dot(NORMAL, LIGHT), 0.0) * (1.0 - shadow);
}