// Original Blender tuft. Opaque blades avoid alpha overdraw and sorting. shader_type spatial; render_mode skip_vertex_transform, cull_disabled, specular_disabled; const int MAX_INTERACTORS = 8; uniform vec2 wind_direction = vec2(1.0, 0.7); uniform vec3 base_color : source_color = vec3(0.23, 0.40, 0.22); uniform vec3 tip_color : source_color = vec3(0.53, 0.67, 0.35); uniform float wind_amount : hint_range(0.0, 1.0) = 0.24; uniform int interactor_count : hint_range(0, 8) = 0; uniform vec3 interactor_positions[MAX_INTERACTORS]; uniform float interaction_radius : hint_range(0.25, 4.0) = 1.8; uniform float interaction_strength : hint_range(0.0, 2.0) = 0.55; uniform int clearing_count = 0; uniform vec4 clearing_segments[4]; uniform vec4 clearing_half_widths = vec4(0.0); varying float blade_height; varying float shade_variation; varying float pressed_weight; void vertex() { blade_height = UV.y; float tip_weight = blade_height * blade_height; vec3 anchor = MODEL_MATRIX[3].xyz; float clearing_scale = 1.0; for (int index = 0; index < 4; index++) { if (index >= clearing_count) { break; } vec2 start = clearing_segments[index].xy; vec2 line = clearing_segments[index].zw - start; float along = clamp(dot(anchor.xz - start, line) / max(dot(line, line), 0.001), 0.0, 1.0); float distance_to_path = length(anchor.xz - start - line * along); clearing_scale *= smoothstep(clearing_half_widths[index], clearing_half_widths[index] + 0.20, distance_to_path); } VERTEX *= clearing_scale; tip_weight *= clearing_scale; vec3 world_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz; float phase = dot(anchor.xz, vec2(0.38, 0.29)); float gust = sin(TIME * 1.15 + phase) * 0.65 + sin(TIME * 0.58 + phase * 0.42) * 0.35; world_vertex.xz += normalize(wind_direction) * gust * wind_amount * tip_weight; shade_variation = sin(anchor.x * 0.30 + anchor.z * 0.21) * 0.5 + 0.5; pressed_weight = 0.0; for (int index = 0; index < MAX_INTERACTORS; index++) { if (index >= interactor_count) { break; } vec2 offset = anchor.xz - interactor_positions[index].xz; float distance_to_actor = length(offset); float influence = 1.0 - smoothstep(0.25, interaction_radius, distance_to_actor); vec2 away = offset / max(distance_to_actor, 0.08); world_vertex.xz += away * influence * interaction_strength * tip_weight; world_vertex.y -= influence * 0.18 * tip_weight; pressed_weight = max(pressed_weight, influence); } VERTEX = (VIEW_MATRIX * vec4(world_vertex, 1.0)).xyz; NORMAL = normalize((VIEW_MATRIX * vec4(0.0, 1.0, 0.0, 0.0)).xyz); } void fragment() { vec3 color = mix(base_color, tip_color, smoothstep(0.05, 1.0, blade_height) * 0.78); color *= mix(0.90, 1.09, shade_variation); ALBEDO = mix(color, base_color, pressed_weight * 0.15); ROUGHNESS = 1.0; } void light() { float band = smoothstep(0.05, 0.16, dot(NORMAL, LIGHT)); DIFFUSE_LIGHT += mix(vec3(0.46, 0.60, 0.52), vec3(1.0, 0.98, 0.85), band) * LIGHT_COLOR * ATTENUATION / PI; }