Stylized Cartoon Grass


Another grass shader. But prettier, optimized, and cartoon-looking



See the repository here, or download it here. Or see the plugin used here. Or follow the next instructions:

You need  to set up 3 texture maps see [Screenshot 2] for an example:

  1. Grass Color. The color of the top of the grass. Looked from above
  2. Terrain Color. The color of the bottom of the grass. Looked from above
  3. Mask Front. The gradient from top-to-bottom color [Screenshot 1]

You can of course use NoiseTexture2D for the top and bottom, and GradientTexture2D for the frontal mask.

You also need as many grass variant textures as you like. Use the one in [Screenshot 3] for example. The rules to make your own grass texture are:

  1. Must be pure color white
  2. Use transparency
  3. Optionally, use black for extra details like a margin as in the preview images

Remember to use a MultimeshInstance3D to scatter your grass. Or, if you allow me, use my scatterer tool to paint beautiful scenes like the ones shown here:

Anyway, let me know if something is broken or any thoughts, optimizations, etc… See ya!


Shader code
shader_type spatial;
render_mode unshaded, shadows_disabled;


uniform bool bilboard_y;
uniform bool enable_margin;
uniform vec3 color_margin:source_color;
uniform vec2 terrain_size;

// Colors of all of the grass instanced from a MultiMesh as seen from the top
uniform sampler2D grass_color:source_color,filter_linear_mipmap,repeat_disable;
uniform sampler2D terrain_color:source_color,filter_linear_mipmap,repeat_disable;

// This must be in grayscale. Usually a GradientTexture2D but can be anything you want, even empty for plain color
uniform sampler2D mask_front:source_color,filter_linear_mipmap,repeat_disable,hint_default_black;
uniform sampler2D grass_variants[TOTAL_GRASS_VARIANTS];

// Only supported for Mobile and Forward+ renderers
instance uniform int variant_index;

void vertex(){
	// I totally stole this line
	VERTEX.x += sin(NODE_POSITION_WORLD.x + TIME * 1.25 + UV.y) * ( 1.0 - UV.y) * 0.08;
		MODELVIEW_MATRIX = VIEW_MATRIX * mat4(vec4(normalize(cross(vec3(0.0, 1.0, 0.0), INV_VIEW_MATRIX[2].xyz)), 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(normalize(cross(INV_VIEW_MATRIX[0].xyz, vec3(0.0, 1.0, 0.0))), 0.0), MODEL_MATRIX[3]);
	// Vertex to world space (from center of texture)
	vec2 world_position = 0.5 + (MODEL_MATRIX * vec4(VERTEX, 1.0)).xz / terrain_size;
	// Coloring the mesh vertices will look exactly the same as coloring the pixels but more performant (assuming there are enough subdivisions along the z-axis)
	vec3 recolor_top = texture(grass_color, world_position).rgb;
	vec3 recolor_root = texture(terrain_color, world_position).rgb;
	float gradient_mask = texture(mask_front, UV).r;
	// Mask the top of the grass with one color, mask the root with another, and add them
	COLOR.rgb = recolor_top * gradient_mask;
	COLOR.rgb += recolor_root * (1.0-gradient_mask);

void fragment() {
	// Apply vertex color to albedo
	// The margin will look awfully if you try to vertex-color it, so is better to process it here
	vec4 margin_mask = texture(grass_variants[variant_index], UV);
		ALBEDO = mix(color_margin, ALBEDO, margin_mask.r);
	// TBH, I don't know why alpha must be multiplied or why it needs the threshold without glitching
	ALPHA *= margin_mask.a;
cartoon, grass, optimized, plugin, sway
The shader code and all code snippets in this post are under MIT license and can be used freely. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from dip

Car Tracks On Snow Or Sand – Using viewport textures and particles

Bloody Pool! – Smooth blood trail

Related shaders

Wandering Clipmap – Stylized Grass

Stylized grass with wind and deformation

Grass with Screen-Space Displacement

Notify of

Inline Feedbacks
View all comments