Stylized Cartoon Grass

WHAT IS THIS?

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

 

HOW TO SET THIS UP?!!

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 up to 4 grass textures. 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: https://github.com/dip000/godot-landscaper

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, cull_disabled; //Use 'cull_back' if billboard Y


// Internally capped at 16
const int TOTAL_GRASS_VARIANTS = 4;

uniform bool enable_details = true;
uniform bool billboard_y = false;
uniform vec3 detail_color:source_color = vec3(0.2);
uniform vec2 world_size = vec2(10.0);
uniform vec2 world_position = vec2(0);

// 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 gray scale. Usually a GradientTexture2D but can be anything you want, even empty for plain color
uniform sampler2D gradient_mask:source_color,filter_linear_mipmap,repeat_disable,hint_default_black;
uniform sampler2D variants[TOTAL_GRASS_VARIANTS];

// 'instance' keyword is only for Vulkan rendering drivers and allows multiple grass variants with the same material
instance uniform int variant_index;


void vertex(){
	// Wind sway
	float root = (1.0 - UV.y);
	vec3 sway;
	sway.x = sin(NODE_POSITION_WORLD.x + TIME * 1.25 + UV.y) * root * 0.10;
	sway.y = sin(NODE_POSITION_WORLD.x + TIME * 0.6 + UV.y) * root * 0.08;
	sway.z = cos(NODE_POSITION_WORLD.z + TIME * 0.45 + UV.y) * root * 0.15;
	
	if(billboard_y){
		VERTEX += sway;
		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]);
	}
	else{
		VERTEX += (vec4(sway, 1.0) * MODEL_MATRIX).xyz;
	}
	
	// Vertex to world space (from center of texture)
	vec2 world_space = world_position + (MODEL_MATRIX * vec4(VERTEX, 1.0)).xz / world_size;
	
	// Coloring the mesh vertices will look exactly the same as coloring the pixels but more performant (asuming there are enough subdivisions along z-axis)
	vec3 recolor_top = texture(grass_color, world_space).rgb;
	vec3 recolor_root = texture(terrain_color, world_space).rgb;
	float mask = texture(gradient_mask, UV).r;
	
	// Mask the top of the grass with one color, mask the root with another, and add them
	COLOR.rgb = recolor_top * mask;
	COLOR.rgb += recolor_root * (1.0-mask);
}

void fragment() {
	// Apply vertex color to albedo
	ALBEDO = COLOR.rgb;
	
	vec4 detail_mask = texture(variants[variant_index], UV);

	// Details will look awfull if you try to vertex-color it, so is better to process it here
	if(enable_details){
		ALBEDO = mix(detail_color, ALBEDO, detail_mask.r);
	}
	
	// Enables alpha scissor
	ALPHA = detail_mask.a;
	ALPHA_SCISSOR_THRESHOLD = 0.8;
}
Tags
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

Bloody Pool! – Smooth blood trail

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

Related shaders

Wandering Clipmap – Stylized Grass

Stylized grass with wind and deformation

Stylized Multimesh Grass Shader

Subscribe
Notify of
guest

5 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Adam
6 months ago

This is beautiful and I really want to be able to use it but I can’t seem to be able to move the area that it allows me to paint the grass in. I can create terrain within the default bounds but there’s no clear way to move the bounds or expand them which is severely limiting. I’m able to paint terrain within the bounds and then add grass to it and then drag the terrain to where I want it in my level but if I want to edit it at all once its outside of those default bounds, it disappears. I also don’t see any clear way to scale the grass. By default it’s too small for what I need. Again, fantastic work though. Very excited to see where this goes.

tommio
tommio
1 month ago

This looks wonderful but I’m using Godot 4.3 and I can’t seem to make this work, I tried installing the plugin and pretty much everything works except for the grass that doesn’t spawn, I’ve tried setting every external resource to the ones included in a way that seemed logical but nothing worked, some directions to help me understand what I’m missing would very much be appreciated 🙂