Stylized Multimesh Grass Shader

Another grass shader with wind animation and player interaction using simple parameters.

 

Set up

  • Create a MultiMeshInstance with your grass mesh.
  • Apply the spatial shader as a material override.
  • Set colors and other parameters
  • Set the wind noise to a Perlin FBM FastNoise

If you want to use textured grass remove the Albedo line in the Fragment Shader.

To set the player position for the shader edit the instance parameter through a script.

func _physics_process(delta):

$MultiMeshInstance3D.set(“instance_shader_parameters/player_position”, player_position)

 

My Parameters
– Top Color: 6ab144
– Bottom Color: 2d5518
– Player Displacement Strength: 0.5
– Player Displacement Size: 0.85
– Wind Direction: (0.4, -0.5, 0.81) -> It must not be Zero at all times! Else the Grass will disappear 
– Wind Strength: 0.4
– Wind Noise Size: 0.06
– Wind Noise Speed: 0.14
– Wind Noise: Seamless Perlin FBM Noise
Gain: 0.5, Lacunarity: 1.5, Octaves: 3

Shader code
shader_type spatial;
render_mode cull_disabled, diffuse_toon, specular_schlick_ggx;
// Nice Shader by @_Malido ^^

uniform vec3 top_color: source_color;
uniform vec3 bottom_color: source_color;
uniform float player_displacement_strength: hint_range(0.0, 1.0, 0.01) = 0.4;
uniform float player_displacement_size: hint_range(0.0, 2.0, 0.01) = 1.0;

uniform vec3 wind_direction; // For displacement effect and noise scroll direction
// Length must be greater then 0, if it is the Mesh will disappear.
// Regulate the Speed and Intensity over the dedicated float variables instead.
// Use a negative y component to give it an extra touch.

uniform float wind_strength: hint_range(0.0, 1.0, 0.01) = 0.3;
uniform sampler2D wind_noise; // Periln FBM Noise looks Best
uniform float wind_noise_size: hint_range(0.0, 1.0, 0.001) = 0.05; // high values dont work well
uniform float wind_noise_speed: hint_range(0.0, 1.0, 0.001) = 0.1;

// Instance the Player Position through a GDScript in the _physics_process
instance uniform vec3 player_position;

void vertex() {
	vec3 world_position = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
	
	vec3 wind_texture = texture(wind_noise, world_position.xz * wind_noise_size + normalize(-wind_direction.xz) * TIME * wind_noise_speed).rgb;
	vec3 wind_vector = (vec4(wind_texture * normalize(wind_direction) * wind_strength, 0.0) * MODEL_MATRIX).xyz;
	
	float player_height = smoothstep(1.0, 0.0, length(player_position.y - world_position.y + 0.3));
	vec3 push_direction = vec3(world_position - player_position) * vec3(1 , -0.3 ,1);
	float player_position_factor = smoothstep(player_displacement_size, 0.0, length(push_direction));
	
	vec3 player_push_vector = (vec4(normalize(push_direction), 0.0) * MODEL_MATRIX).xyz;
	
	// Apply Player Position displacement
	VERTEX += player_push_vector * (1.0 - UV.y) * player_position_factor * player_displacement_strength * player_height;
	// Apply Wind displacement (Mixes linear with exponential displacement)
	VERTEX += wind_vector * mix(2.0 * (pow(2.0, -UV.y)-0.5), 1.0 - UV.y, 0.5) * (1.0 - player_position_factor * 0.7);
	
}

void fragment() {
	vec3 color = mix(bottom_color, top_color, 1.0 - UV.y);
	ALBEDO = color;
	ROUGHNESS = 0.3;
	
	// Increase the Specular with Grass Height
	SPECULAR *=  (1.0 - UV.y);
	
	// I had issues with the Normals imported from Blender,
	// so I just set the Normals facing up manually.
	// You can disable this line if you have no normal shading problems
	NORMAL_MAP = vec3(0, 1, 0);
}
Tags
3d, displacement, grass, Interaction, optimized, stylized, sway, wind
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

Related shaders

Wandering Clipmap – Stylized Grass

Stylized grass with wind and deformation

Stylized Cartoon Grass

Subscribe
Notify of
guest

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
bujupah
bujupah
24 days ago

Can you please share an example? I am bit confused on how to set this up!