|
shader_type spatial; |
|
render_mode unshaded, blend_mix, depth_draw_never, cull_disabled, shadows_disabled; |
|
|
|
uniform sampler2D cloud_noise : repeat_enable, filter_linear_mipmap; |
|
uniform float noise_scale : hint_range(500.0, 20000.0) = 4000.0; |
|
uniform vec2 wind_direction = vec2(1.0, 0.3); |
|
uniform float wind_speed : hint_range(0.0, 100.0) = 12.0; |
|
uniform float churn_speed : hint_range(0.0, 0.2) = 0.02; |
|
uniform float warp_strength : hint_range(0.0, 1.0) = 0.35; |
|
uniform float coverage : hint_range(0.0, 1.0) = 0.5; |
|
uniform float softness : hint_range(0.01, 1.0) = 0.25; |
|
uniform vec4 lit_color : source_color = vec4(1.0, 1.0, 1.0, 1.0); |
|
uniform vec4 shadow_color : source_color = vec4(0.55, 0.6, 0.7, 1.0); |
|
uniform vec3 sun_direction = vec3(-0.4, -0.7, -0.3); |
|
uniform float sun_shading : hint_range(0.0, 12.0) = 4.0; |
|
uniform float sun_sample_distance : hint_range(10.0, 1000.0) = 250.0; |
|
uniform float scatter_strength : hint_range(0.0, 2.0) = 0.6; |
|
uniform float scatter_power : hint_range(1.0, 64.0) = 12.0; |
|
uniform float opacity : hint_range(0.0, 1.0) = 0.85; |
|
uniform float fade_start = 15000.0; |
|
uniform float fade_end = 30000.0; |
|
|
|
// Double-precision camera-relative rendering: MODEL_MATRIX translation is not absolute world |
|
// space, so anchor the noise to the plane's local coords and measure distance in view space. |
|
varying vec3 plane_pos; |
|
|
|
void vertex() { |
|
plane_pos = VERTEX; |
|
} |
|
|
|
float fbm(vec2 uv, float t) { |
|
float d = texture(cloud_noise, uv).r * 0.55; |
|
d += texture(cloud_noise, uv * 2.7 + vec2(t, -t * 0.6)).r * 0.3; |
|
d += texture(cloud_noise, uv * 6.3 - vec2(t * 1.7, t)).r * 0.15; |
|
return d; |
|
} |
|
|
|
void fragment() { |
|
float t = TIME * churn_speed; |
|
vec2 uv = (plane_pos.xz + wind_direction * TIME * wind_speed) / noise_scale; |
|
|
|
vec2 warp = vec2( |
|
texture(cloud_noise, uv * 0.6 + vec2(t * 0.4, 0.0)).r, |
|
texture(cloud_noise, uv * 0.6 + vec2(0.37, t * 0.4 + 0.5)).r |
|
) - 0.5; |
|
vec2 wuv = uv + warp * warp_strength; |
|
|
|
float cov_mod = mix(0.85, 1.25, texture(cloud_noise, uv * 0.11).r); |
|
float threshold = mix(0.85, 0.25, coverage); |
|
|
|
float d_raw = fbm(wuv, t) * cov_mod; |
|
float density = smoothstep(threshold, threshold + softness, d_raw); |
|
|
|
vec3 sun = normalize(sun_direction); |
|
vec2 sun_offset = sun.xz * (sun_sample_distance / noise_scale); |
|
float d_sun_raw = fbm(wuv - sun_offset, t) * cov_mod; |
|
|
|
float light = clamp(1.0 - (d_sun_raw - d_raw) * sun_shading, 0.0, 1.0); |
|
light *= mix(1.0, 0.7, density); |
|
vec3 col = mix(shadow_color.rgb, lit_color.rgb, light); |
|
|
|
vec3 view_dir = normalize(mat3(INV_VIEW_MATRIX) * VERTEX); |
|
float fwd = pow(max(dot(view_dir, sun), 0.0), scatter_power); |
|
col += fwd * scatter_strength * (1.0 - density) * lit_color.rgb; |
|
|
|
float fade = 1.0 - smoothstep(fade_start, fade_end, length(VERTEX)); |
|
|
|
ALBEDO = col; |
|
ALPHA = density * opacity * fade; |
|
} |