shader_type spatial; render_mode specular_disabled, cull_disabled, ambient_light_disabled; // --- GLOBAL UNIFORMS (Vento e Neve) --- global uniform float wind_scale; global uniform float wind_speed; global uniform float wind_strength; global uniform vec3 wind_direction; global uniform sampler2D wind_noise : filter_linear_mipmap; global uniform float global_snow_amount; // --- PARAMETRI ESTETICI --- uniform bool billboard_enabled = true; uniform bool wind_enabled = true; uniform vec4 base_color : source_color = vec4(0.2, 0.6, 0.3, 1.0); uniform sampler2D alpha_texture : source_color, filter_nearest; uniform float leaves_scale = 1.0; uniform float rotation_degrees = 0.0; uniform vec2 texture_offset = vec2(0.0, 0.0); uniform float opacity : hint_range(0.0, 1.0) = 1.0; // --- PARAMETRI CHIAZZE ERBA (NOISE PATCHES) --- // Inserisci qui una texture Noise (es. FastNoiseLite in Godot) uniform sampler2D grass_noise : hint_default_white, filter_linear_mipmap; uniform float noise_scale = 0.05; // Più è basso, più le chiazze sono grandi uniform vec4 patch_color : source_color = vec4(0.1, 0.35, 0.2, 1.0); // Colore scuro della chiazza uniform float patch_threshold : hint_range(0.0, 1.0) = 0.5; // Regola quanto sono estese le chiazze uniform float patch_smoothness : hint_range(0.01, 1.0) = 0.2; // Morbidezza sfumatura ai bordi uniform float patch_intensity : hint_range(0.0, 1.0) = 0.85; // Quanto è forte il colore della chiazza uniform vec4 snow_color : source_color = vec4(0.85, 0.9, 0.95, 1.0); // --- CONTROLLO GRADIENTE E RANDOM --- uniform float height_min = 0.0; uniform float height_max = 5.0; uniform float shadow_intensity : hint_range(0.0, 1.0) = 0.5; uniform float highlight_intensity : hint_range(0.0, 1.0) = 0.3; uniform float light_steps : hint_range(1.0, 10.0) = 4.0; uniform float random_mix : hint_range(0.0, 1.0) = 0.3; uniform float cast_shadow_strength : hint_range(0.0, 1.0) = 0.6; varying vec3 v_final_color; varying float v_shade_factor; float hash(vec3 p) { p = fract(p * 0.1031); p += dot(p, p.yzx + 33.33); return fract((p.x + p.y) * p.z); } void vertex() { vec3 instance_pos = MODEL_MATRIX[3].xyz; // --- LOGICA VENTO --- float total_angle = radians(rotation_degrees); if (wind_enabled) { float time = TIME * wind_speed; vec2 noise_uv = (instance_pos.xz * wind_scale) + (time * wind_direction.xz * 0.5); float noise_val = textureLod(wind_noise, noise_uv, 2.0).r; float sway = sin(time + (noise_val * 10.0)); total_angle += (sway * wind_strength); } // --- CALCOLO COLORE BASE --- float h_factor = clamp((instance_pos.y - height_min) / (height_max - height_min), 0.0, 1.0); float leaf_rand = hash(instance_pos); float combined_factor = mix(h_factor, leaf_rand, random_mix); combined_factor = ceil(combined_factor * light_steps) / light_steps; v_shade_factor = combined_factor; vec3 dark_color = base_color.rgb * (1.0 - shadow_intensity); vec3 light_color = base_color.rgb + (vec3(1.0) - base_color.rgb) * highlight_intensity; vec3 base_grass_color = mix(dark_color, light_color, combined_factor); // ---> LOGICA CHIAZZE ERBA (NOISE) <--- // Campioniamo il noise usando la posizione XZ del singolo ciuffo d'erba nel mondo. // Usiamo textureLod con 0.0 perché siamo nel vertex shader. float n_val = textureLod(grass_noise, instance_pos.xz * noise_scale, 0.0).r; // Creiamo la maschera per la chiazza: liscia ai bordi, solida al centro. float patch_mask = smoothstep(patch_threshold - patch_smoothness, patch_threshold + patch_smoothness, n_val); // Mescoliamo il colore base dell'erba con il colore scuro della chiazza v_final_color = mix(base_grass_color, patch_color.rgb, patch_mask * patch_intensity); // --- LOGICA TRASFORMAZIONE --- if (billboard_enabled) { vec3 view_pos_origin = (VIEW_MATRIX * vec4(instance_pos, 1.0)).xyz; vec2 offset = (UV - 0.5) * leaves_scale; float c = cos(total_angle); float s = sin(total_angle); vec2 rotated_offset = vec2(offset.x * c - offset.y * s, offset.x * s + offset.y * c); vec3 final_pos = view_pos_origin + vec3(rotated_offset.x, rotated_offset.y, 0.0); MODELVIEW_MATRIX = mat4(1.0); MODELVIEW_MATRIX[3].xyz = final_pos; VERTEX = vec3(0.0); } else { float c = cos(total_angle); float s = sin(total_angle); vec2 local_v = (VERTEX.xy) * leaves_scale; VERTEX.x = local_v.x * c - local_v.y * s; VERTEX.y = local_v.x * s + local_v.y * c; } } void fragment() { vec2 shifted_uv = UV + texture_offset; vec4 tex = texture(alpha_texture, shifted_uv); // Trucco utile per non renderizzare i bordi trasparenti dei piani (opzionale) if (tex.r * opacity < 0.5) { discard; } float top_mask = 1.0 - shifted_uv.y; float snow_mask = smoothstep(1.0 - global_snow_amount, 1.2 - global_snow_amount, top_mask); snow_mask *= step(0.01, global_snow_amount); vec3 dark_snow = snow_color.rgb * (1.0 - shadow_intensity); vec3 shaded_snow = mix(dark_snow, snow_color.rgb, v_shade_factor); vec3 final_albedo = mix(v_final_color, shaded_snow, snow_mask); ALBEDO = final_albedo; // ALPHA = tex.r * opacity; -> Disabilitato se usi 'discard' sopra. // ALPHA_SCISSOR_THRESHOLD = 0.5; -> Idem ROUGHNESS = 0.02; } void light() { float shadow_factor = mix(1.0 - cast_shadow_strength, 1.0, ATTENUATION); float cutoff_mask = smoothstep(0.0, 0.05, ATTENUATION); DIFFUSE_LIGHT += (shadow_factor * LIGHT_COLOR) * cutoff_mask; }