// Adapted for The Steward from the bundled Terrain3D particle-grass example. shader_type spatial; render_mode skip_vertex_transform, cull_disabled, blend_mix; const int MAX_INTERACTORS = 8; uniform vec2 wind_direction = vec2(1.0, 0.7); uniform vec3 base_color : source_color = vec3(0.16, 0.31, 0.08); uniform vec3 tip_color : source_color = vec3(0.54, 0.68, 0.24); uniform float wind_amount : hint_range(0.0, 1.0) = 0.34; 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.75; varying vec4 grass_data; varying float pressed_weight; void vertex() { grass_data[2] = VERTEX.y + 0.55; grass_data[2] *= grass_data[2]; VERTEX.xz *= 1.0 - grass_data[2]; COLOR = mix(COLOR, vec4(1.0), smoothstep(0.9, 1.0, grass_data[2])); COLOR *= INSTANCE_CUSTOM[3] < 0.35 ? 1.0 : mix(1.0, 0.75, smoothstep(0.35, 0.0, grass_data[2])); grass_data.rg = INSTANCE_CUSTOM.rg; vec3 world_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz; float scale = pow(INSTANCE_CUSTOM[3] * INSTANCE_CUSTOM[3], 0.707); float force = INSTANCE_CUSTOM[2] * grass_data[2] * scale * wind_amount; force -= fract(force * 256.0) * force * 0.05; force = pow(max(force, 0.0), 0.707); float lateral_wobble = sin(TIME * 2.0 * (1.0 + grass_data.r + grass_data.g)) * 0.2 * (1.0 - INSTANCE_CUSTOM[2]); vec2 direction = normalize(wind_direction); world_vertex.xz -= (vec2(-direction.y, direction.x) * lateral_wobble + direction) * force; world_vertex.y -= INSTANCE_CUSTOM[2] * force * grass_data[2]; grass_data[3] = force; pressed_weight = 0.0; for (int index = 0; index < MAX_INTERACTORS; index++) { if (index >= interactor_count) { break; } vec2 offset = world_vertex.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 * grass_data[2]; world_vertex.y -= influence * 0.28 * grass_data[2]; pressed_weight = max(pressed_weight, influence); } VERTEX = (VIEW_MATRIX * vec4(world_vertex, 1.0)).xyz; NORMAL = MODELVIEW_NORMAL_MATRIX * NORMAL; BINORMAL = MODELVIEW_NORMAL_MATRIX * BINORMAL; TANGENT = MODELVIEW_NORMAL_MATRIX * TANGENT; } void fragment() { vec3 color = mix(base_color, tip_color, grass_data[2] * 0.62); color.rg *= grass_data.rg * 0.18 + 0.91; color *= pow(COLOR.rgb, vec3(2.2)); ALBEDO = mix(color, base_color * 0.72, pressed_weight * 0.42); float specular_roughness = clamp(max(grass_data[2], grass_data[3]), 0.0, 1.0); ROUGHNESS = 1.0 - specular_roughness * 0.22; SPECULAR = clamp(specular_roughness * 0.2, 0.0, 0.12); BACKLIGHT = tip_color * 0.24; }