Enrich
Enrich is a system built in Unity that allows the user to paint grass onto any surface. By using a couple of shaders and leveraging the UV maps produced by Unity’s lightmapper, we can make paintable grass that requires almost no setup. All that is required to make a model paintable is enabling lightmap UVs, placing the object under a parent with a special Sector script enabled, and tagging the object as paintable.
The system was originally built for a game concept where the player would be able to enrich the world around them, taking the levels from dead, barren, and industrialized, to lush, green, and alive. A large focus of the concept revolves around music and rhythm. The player can speed through the world, fighting enemies in time to the rhythm. The enriched land they spread changes the music of the land. In baren areas the music is dark and mechanical, but in enriched areas the music is upbeat and full of life. The enriched land itself bobs and weaves to the beat of the music, tying everything together. This is in part inspired by the way the background in Move or Die moves to the beat of the music.
The visualization of the enriched areas is incredibly important and needed to be easy to do. I didn’t want to be making two of each asset, one enriched and one not. Using a shader allows me to make a single model and two sets of textures. One for the barren state, and one for the enriched state. The shader then simply lerps between them as the object is taken over by grass.
Technical Details
The world is divided up into paintable regions, or sectors, with each sector having a paint map texture. This texture is used as a mask for the grass shader. The demo above shows only one region, but adding more is as simple as adding a Region script to a new object.
The painting works in 3 main steps. Compiling UVs, Generating world space maps, and painting. First a manager class collects up all of its children tagged with paintable. The manager then packs the UVs for each object into one big set of UVs. This means we only have to use one texture to represent the paint map for the whole region. These UVs are sent to a shader which renders the world space coordinates of each fragment to another texture, which we will call the ‘world space map’. Below is the worldspace map and paint map corresponding to the demo above.
World space map
Paint map
Now, when we want to paint a sphere onto the terrain, we feed the center and radius into a third shader which tests each pixel of the worldspace map to see if it is inside the sphere. Every fragment that matches has the corresponding fragment in the paint map colored. You can see above that the paint map on the right has regions painted red that correspond to regions on the world space map. The grass shader takes the paint map in as a texture, and uses the map as a mask.
The grass shader is a simple geometry shader. The shader simply outputs 5 camera facing triangles per input triangle with a the peak vertex being offset by wind and scaled by the mask strength. This applies to forward add, forward base, and shadowcaster passes, ensuring we get directional, spot and point lights.
References
House model from: https://assetstore.unity.com/packages/3d/environments/fantasy/baker-s-house-26443