Godot Version
4.7.1
Question
Following this grass tutorial: Grass Rendering Series Part 3: Animating and Interacting With Grass in Godot | hexaquo
Thanks to a forum member the object interaction with grass is now working. However, I’d like the shader to read the player position and use that for grass interaction instead. I’ve spent the past couple of hours trying to get it to work and referenced other solutions and no dice.
Currently the grass is a MultiMeshInstance3D with a MeshInstance3D Sphere as a child to the MultiMesh which updates the Object Position value, giving the grass the pressed down look.
I intend on making multiple MultiMesh grass patches and would like the player to be able to interact with these separate patches without each patch needing it’s own child sphere mesh, I assume this would be unnecessarily more costly compared to updating the shader Object Position parameter with the player position.
For reference, the sphere on the right is what’s currently implemented, I’d like to transfer this over to the player controller on the left.
Code for the grass shader:
shader_type spatial;
render_mode cull_disabled;
uniform float size_small = 0.2;
uniform float size_large = 0.6;
uniform float blade_bend = 0.5;
uniform vec3 color_small: source_color = vec3(0.3, 0.6, 0.1);
uniform vec3 color_large: source_color = vec3(0.9, 0.9, 0.2);
uniform sampler2D patch_noise;
uniform float patch_scale = 5.0;
varying float patch_factor;
uniform sampler2D wind_noise;
uniform float wind_strength = 0.1;
uniform vec2 wind_direction = vec2(1.0, 0.0);
uniform float wind_bend_strength = 2.0;
uniform float wind_ao_affect = 1.5;
uniform float object_radius = 1.0;
uniform vec3 object_position;
varying float bottom_to_top;
varying float current_wind_bend;
varying float cut_height;
void vertex() {
cut_height = 1.0;
// Cutting all grass on the right
if (NODE_POSITION_WORLD.x > 0.0) {
cut_height = 0.5;
VERTEX.y = min(VERTEX.y, cut_height);
UV.y = max(UV.y, 1.0 - cut_height);
}
bottom_to_top = 1.0 - UV.y;
// Wind logic
vec2 wind_position = NODE_POSITION_WORLD.xz / 10.0;
wind_position -= (TIME + 8.0) * wind_direction * wind_strength;
current_wind_bend = texture(wind_noise, wind_position).x;
current_wind_bend *= wind_strength;
current_wind_bend *= bottom_to_top * 2.0;
mat4 inv_model = inverse(MODEL_MATRIX);
vec2 local_direction = (inv_model * vec4(wind_direction.x, 0.0, wind_direction.y, 0.0)).xz;
VERTEX.xz += current_wind_bend * local_direction * wind_bend_strength;
// Bend away from the object
float object_distance = distance(object_position, NODE_POSITION_WORLD);
float bend_away_strength = max(object_radius - object_distance, 0.0) / object_radius;
vec2 bend_direction = normalize(object_position.xz - NODE_POSITION_WORLD.xz);
VERTEX.xz -= (inv_model * vec4(bend_direction.x, 0.0, bend_direction.y, 0.0)).xz
* bend_away_strength * bottom_to_top;
VERTEX.y -= bend_away_strength * bottom_to_top * 0.5;
// General appearance
VERTEX.z += blade_bend * pow(bottom_to_top, 2.0);
patch_factor = texture(patch_noise, NODE_POSITION_WORLD.xz / patch_scale).r;
VERTEX *= mix(size_small, size_large, patch_factor);
NORMAL = mix(NORMAL, vec3(0.0, 1.0, 0.0), bottom_to_top);
}
void fragment() {
if (!FRONT_FACING) NORMAL = -NORMAL;
AO = bottom_to_top - current_wind_bend * wind_ao_affect;
AO_LIGHT_AFFECT = cut_height;
ALBEDO = mix(color_small, color_large, patch_factor);
BACKLIGHT = vec3(0.2);
ROUGHNESS = 0.4;
SPECULAR = 0.2;
}
Script hooked into the MultiMesh:
@tool
extends MultiMeshInstance3D
func _process(delta: float) → void:
if has_node(“MeshInstance3D”):
material_override.set_shader_parameter(“object_position”, $MeshInstance3D.global_position)
Any help would be much appreciated.





