117 lines
3.5 KiB
Plaintext
117 lines
3.5 KiB
Plaintext
shader_type spatial;
|
|
render_mode blend_mix, cull_back, depth_draw_opaque;
|
|
|
|
global uniform float global_snow_start_time = -1.0;
|
|
global uniform float global_snow_accumulation_speed = 0.005;
|
|
global uniform float global_snow_melt_time = -1.0;
|
|
global uniform float global_snow_melt_speed = 0.1;
|
|
global uniform float global_snow_amount = 0.0;
|
|
global uniform vec4 global_snow_color = vec4(0.92, 0.96, 1.0, 1.0);
|
|
|
|
uniform float max_height : hint_range(0.02, 1.0) = 0.2;
|
|
uniform float half_width : hint_range(0.01, 20.0) = 1.0;
|
|
uniform float half_depth : hint_range(0.01, 20.0) = 1.0;
|
|
uniform float noise_scale : hint_range(0.05, 2.0) = 0.35;
|
|
uniform float noise_strength : hint_range(0.0, 0.4) = 0.18;
|
|
uniform float edge_rounding : hint_range(0.0, 0.3) = 0.12;
|
|
uniform float side_shade : hint_range(0.2, 1.0) = 0.82;
|
|
uniform float sparkle_strength : hint_range(0.0, 0.1) = 0.03;
|
|
|
|
varying float v_snow_amount;
|
|
varying float v_noise;
|
|
|
|
float hash(vec2 p) {
|
|
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
|
|
}
|
|
|
|
float value_noise(vec2 p) {
|
|
vec2 i = floor(p);
|
|
vec2 f = fract(p);
|
|
vec2 u = f * f * (3.0 - 2.0 * f);
|
|
|
|
float a = hash(i);
|
|
float b = hash(i + vec2(1.0, 0.0));
|
|
float c = hash(i + vec2(0.0, 1.0));
|
|
float d = hash(i + vec2(1.0, 1.0));
|
|
|
|
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
|
|
}
|
|
|
|
float fbm(vec2 p) {
|
|
float value = 0.0;
|
|
float amplitude = 0.5;
|
|
float frequency = 1.0;
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
value += value_noise(p * frequency) * amplitude;
|
|
frequency *= 2.0;
|
|
amplitude *= 0.5;
|
|
}
|
|
|
|
return clamp(value, 0.0, 1.0);
|
|
}
|
|
|
|
float get_snow_amount() {
|
|
float snow_amount = clamp(global_snow_amount, 0.0, 1.0);
|
|
if (global_snow_start_time >= 0.0) {
|
|
float timed_amount = clamp(
|
|
(TIME - global_snow_start_time) * global_snow_accumulation_speed,
|
|
0.0,
|
|
1.0
|
|
);
|
|
snow_amount = max(snow_amount, timed_amount);
|
|
}
|
|
if (global_snow_melt_time >= 0.0) {
|
|
float melt = clamp((TIME - global_snow_melt_time) * global_snow_melt_speed, 0.0, 1.0);
|
|
snow_amount = min(snow_amount, 1.0 - melt);
|
|
}
|
|
return snow_amount;
|
|
}
|
|
|
|
void vertex() {
|
|
vec3 base_vertex = VERTEX;
|
|
vec3 world_pos = (MODEL_MATRIX * vec4(base_vertex, 1.0)).xyz;
|
|
float snow_amount = get_snow_amount();
|
|
float noise_val = fbm(world_pos.xz * noise_scale);
|
|
float thickness = max_height * snow_amount * mix(
|
|
1.0 - noise_strength,
|
|
1.0 + noise_strength,
|
|
noise_val
|
|
);
|
|
float footprint_growth = smoothstep(0.0, 0.35, snow_amount);
|
|
|
|
float from_bottom = clamp((base_vertex.y + max_height * 0.5) / max_height, 0.0, 1.0);
|
|
VERTEX.y = -max_height * 0.5 + thickness * from_bottom;
|
|
VERTEX.x *= mix(0.92, 1.0, footprint_growth);
|
|
VERTEX.z *= mix(0.94, 1.0, footprint_growth);
|
|
|
|
float edge_x = abs(base_vertex.x) / max(half_width, 0.001);
|
|
float edge_z = abs(base_vertex.z) / max(half_depth, 0.001);
|
|
float edge_mask = smoothstep(0.55, 1.0, max(edge_x, edge_z));
|
|
float top_mask = smoothstep(0.6, 1.0, from_bottom) * snow_amount;
|
|
float round_factor = edge_rounding * edge_mask * top_mask;
|
|
VERTEX.x *= 1.0 - round_factor;
|
|
VERTEX.z *= 1.0 - round_factor;
|
|
|
|
v_snow_amount = snow_amount;
|
|
v_noise = noise_val;
|
|
}
|
|
|
|
void fragment() {
|
|
if (v_snow_amount <= 0.001) {
|
|
ALPHA = 0.0;
|
|
ALBEDO = global_snow_color.rgb;
|
|
ROUGHNESS = 1.0;
|
|
SPECULAR = 0.0;
|
|
} else {
|
|
float facing_up = clamp(NORMAL.y, 0.0, 1.0);
|
|
float shade = mix(-sparkle_strength, sparkle_strength, v_noise);
|
|
vec3 snow_albedo = clamp(global_snow_color.rgb + vec3(shade), 0.0, 1.0);
|
|
|
|
ALBEDO = mix(snow_albedo * side_shade, snow_albedo, facing_up);
|
|
ROUGHNESS = mix(0.95, 0.72, facing_up);
|
|
SPECULAR = 0.18;
|
|
ALPHA = smoothstep(0.0, 0.28, v_snow_amount);
|
|
}
|
|
}
|