Grass Patch 3D

A procedural grass background shader with no texture dependency.

Two-layer noise — large-scale patches (noise_scale) blended with fine grain (detail_detail) controls color variation across the surface

World-anchored UVs — reconstructs world position from camera_world_pos + camera_zoom uniforms so the grass stays fixed in world space as the camera moves

Wind — shifts the UV by TIME * wind_speed * wind_direction each frame, giving a slow moving motion

Color — lerps between color_base and color_highlight based on the combined noise value, with color_variation controlling the contrast range

Shader code
shader_type canvas_item;

// --- Appearance ---
uniform vec4 color_base : source_color = vec4(0.28, 0.55, 0.15, 1.0);
uniform vec4 color_highlight : source_color = vec4(0.45, 0.72, 0.25, 1.0);
uniform float color_variation : hint_range(0.0, 1.0) = 0.35;

// --- Noise / Texture ---
uniform float noise_scale : hint_range(1.0, 80.0) = 25.0;
uniform float detail_scale : hint_range(10.0, 200.0) = 80.0;
uniform float detail_strength : hint_range(0.0, 1.0) = 0.3;

// --- Wind Movement ---
uniform float wind_speed : hint_range(0.0, 2.0) = 0.4;
uniform float wind_strength : hint_range(0.0, 0.02) = 0.005;
uniform vec2 wind_direction = vec2(1.0, 0.3);

// --- Camera (set by background_grass.gd each frame) ---
uniform vec2 camera_world_pos = vec2(0.0, 0.0);
uniform float camera_zoom = 1.0;

// Cheap hash-based noise (no texture needed)
float hash(vec2 p) {
	vec3 p3 = fract(vec3(p.xyx) * 0.1031);
	p3 += dot(p3, p3.yzx + 33.33);
	return fract((p3.x + p3.y) * p3.z);
}

float value_noise(vec2 p) {
	vec2 i = floor(p);
	vec2 f = fract(p);
	f = f * f * (3.0 - 2.0 * f); // smoothstep

	float a = hash(i);
	float b = hash(i + vec2(1.0, 0.0));
	float c = hash(i + vec2(0.0, 1.0));
	float d = hash(i + vec2(1.0, 1.0));

	return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
}

void fragment() {
	// Reconstruct world-anchored UV from camera position + zoom
	vec2 screen_size = 1.0 / SCREEN_PIXEL_SIZE;
	vec2 uv_world = camera_world_pos / screen_size + (UV - 0.5) / camera_zoom;

	// Wind offset applied in world space
	vec2 wind = normalize(wind_direction) * TIME * wind_speed;
	vec2 uv_shifted = uv_world + wind * wind_strength;

	// Large-scale color variation
	float n1 = value_noise(uv_shifted * noise_scale);

	// Fine detail layer
	float n2 = value_noise(uv_shifted * detail_scale);

	// Combine: large patches + fine grain
	float blend = n1 * (1.0 - detail_strength) + n2 * detail_strength;
	blend = mix(0.5 - color_variation * 0.5, 0.5 + color_variation * 0.5, blend);

	// Final color
	COLOR = mix(color_base, color_highlight, blend);
}
Live Preview
Tags
3d grass, animated grass, grass
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.

More from al77ex@gmail.com

Related shaders

guest

0 Comments
Oldest
Newest Most Voted