Featured Blog | This community-written post highlights the best of what the game industry has to offer. Read more like it on the Game Developer Blogs or learn how to Submit Your Own Blog Post
Generating Optimized 2D Art in Unity: Procedural Generation for Stylized and Optimized 2D Foliage
A quick look at 2d procedural foliage generation in Unity, with a focus on achieving better aesthetics and performance optimization. A great starting point for PCG beginners.
Note: This article was originally published on LinkedIn. If you enjoy my article, please click through and consider sharing on social media and connecting with me!
Games need detailed background art. While generally not central to the gameplay, quality and varied background art is needed for storytelling, immersion, visual appeal, and general polish. A lack of variety in background trees is noticeable in a game. Creating a variety of trees by hand takes a lot of effort, and each variation needs bespoke work from an Artist.
Procedural Generation can be used for this problem by reducing the need for bespoke content creation. As a developer, you generally won’t have complete control over your Procedural generation work. This makes it particularly well suited for such background content – content that is important but won’t break the game if it isn’t entirely perfect.
A programmatic approach to foliage also allows for a specific look and style to be created through parameters, and then further varied slightly using those parameters.
LSystems For Foliage Generation
LSystems have been used extensively for foliage generation. The algorithm is naturally suited to producing branching trees and plants. It allows for a degree of randomization and lets you control the aesthetics of what you generate through the use of parameters and rules.
Basic LSystems are easy. Branching, length, angle, and iteration count covers this.
But What Can You Do To Generate More Aesthetic Artwork?
I loved the aesthetic of Kate Compton’s work and modeled my own after it, with the added criteria that whatever I create must be suitable for real-time simulation in Unity, so it could be used as a tool for art generation for 2D Unity games. Kate has shared her foliage PCG work as well as guides for those starting out with PCG - http://www.galaxykate.com/blog/generator.html
I added the following parameters:
Iterations
Branch angles
Branch width
Max branch length
Max leaf length
Base colour
Colour variation
Saturation
Leaf shapes
Leaf aspect ratio
Petal count
Petal size
Petal shape
Petal base colour
Petal saturation
Branch spikiness
Branch taper
Adding Visual Variance
What if you want more variety for each generated tree, so there aren’t just duplicates all over?
Generate a forest by “changing it a little”. Take the base model, stored in a structure (DNA), and then change relevant parameters by just a little to generated a similar tree of the same species. Repeat x10 or x100 and you’ve got a forest that’s uniform but not full of the same cloned tree.
Beyond this, Kate also shares some general principles for more visual variety when you’re using PCG techniques.
Here are some ideas she suggests for improving distribution, so everything you generate looks natural and has some variance.
1. Barnacling - Surround large objects with a few smaller or maybe even a lot of tiny ones to give it a more natural appearance.
2. Footing – Make sure everything you generate looks like it fits in with the world, basically by handling where objects would interact (In this case the tree branch and the ground would overlap in the form of roots)
3. Greebling – Originally used by those working at Industrial Light & Magic for the special effects of Star Wars. Add small details to large surfaces to make them appear more complex and interesting.
After adding smaller, slightly changed trees next to each one:
To add to this, you can also simulate growth - Increase branch length, leaf count, flower count, and finally iteration count over time.
Animation
I went with the obvious approach first – just manipulating mesh position and rotation using a sine wave. I apply this with a wind strength variable relative to the distance from the base of the tree – so basically, the root doesn’t move at all, and the treetops will move the most. It looks good enough for a stylized 2D game.
I later used Shader Graph to move all the animation to the GPU – manipulating vertex position using shader code works just as well and results in much better performance.
Data Structures - Plant DNA
Generation is randomized but deterministic. If you use the same parameters, you’ll get the same tree again. In a way, the structure of parameters is a tree’s DNA.
Generate a tree you like? Save it’s DNA. Load it later to re-use in your game.
Taking this further, you can cross-breed and evolve species using Genetic Algorithms. The tree DNA is already defined as a struct of floats.
Simply put, use a genetic algorithm to combine two halves of DNA together and then mutate them to create a new species. Then set it up so a designer can select two trees they like and blend them together.
You can extend this concept into gameplay – add some player generated content by allowing players to generate their own plants, save them, evolve them, re-plant them, and so on.
How Do You Optimize Mesh Generation For Generating And Simulating Foliage In Real-Time In Unity
Generation is slow and does take some time. As long as the code isn’t unnecessarily complex, you can get 300 trees in about a minute and maybe add a loading screen if you need to do this during gameplay.
But the generated trees have to be optimized for performance as well. Background art is exactly that- background. You can’t have too much CPU/GPU work and draw calls being taken up by background art – it’s needed for foreground art, animations, shaders, lighting, AI, gameplay, etc.
The branches are individual sprites and there aren’t that many of them.
The leaves are generated meshes, which have 4 vertices each. This is necessary for the stylized look and variety. This is where most of the rendering time is spent, so that’s where optimization has to be focused.
Here’s how I handled optimization:
Shared materials
Each leaf has the same set of parameters and colours are determined within the shader based on the pixel’s distance to the ground. This means that a single instance of a material can be shared by each leaf in a tree.
Optimized shaders
For starters, use Unity’s URP and associated unlit URP shaders.
Shader-based wind animation
The GPU tends to be underused compared to the CPU, so it’s almost always beneficial to move complex computations to the GPU. In this case, it’s better to simulate wind through applying a sine function to the mesh vertices (I love Shader Graph for this). The alternative would be changing object positions and rotations using C# code, which will almost always perform worse.
Combine meshes to reduce draw calls further
I discovered a couple years ago during a different project (VR Educational Games for the oil and gas industry) that Unity’s Static and Dynamic batching are inefficient. I assumed that the number of meshes in a scene wouldn’t matter, just the triangle and vertex count. It turns out, you can usually improve performance by quite a bit by combining meshes wherever possible, rather than solely relying on batching.
I love the Simple Mesh Combine asset from the Unity Asset Store for this.
Before combining meshes:
After combining meshes:
Draw Calls reduced to around 33%, Batches reduced to 0.5% of the initial count.
CPU and GPU time almost halved, which can loosely be translated to mean that performance and frame rate will be 2x better.
Combined, unanimated
This still takes up a decent amount of rendering time because of the number of meshes used. Framerates are still okay, but could be a problem if you’re targeting low-end hardware or need other more expensive computation for graphics, AI, or gameplay.
Billboards For Performance – Maybe Not In This Case?
If you can get away without animations, you don’t need individual meshes anymore. I wanted to test out using Render Texture to produce billboards rather than rendering trees with hundreds of leaf meshes.
Here’s what my script does:
Isolate trees to their own layer and make sure the render to texture camera only sees that layer (Culling mask).
Center the camera on the tree.
Render what the camera sees to a texture.
Apply this texture to a sprite.
Disable the tree and replace it with a single sprite.
This is slow though. Each tree has to be rendered individually, one a frame. Takes about 10 minutes for a 100 trees. The result reduces draw calls drastically and brings down frame time.
This didn’t work as expected for me – draw calls, triangles, and vertices are reduced, but the actual frame rendering time didn’t go down. So in the end, combining meshes seemed to give me better performance.
Good Tool Design
For this project, my focus was on being able to iterate and prototype quickly, and not so much on the quality or usability of the end product. Still, I gave a little bit of thought to tool design:
A single struct for plant DNA – I'm reiterating, but this makes for good tool design as well as all parameters that affect generation are stored and accessible together.
Each parameter has fixed constraints both in the code and in the editor UI – Because of the unique characteristics each parameter is responsible for, setting these constraints makes it easier to iterate further as well as use the tool. This ensures everything you’re generating still resembles the shapes you’re going for.
Unity UI sliders for each parameter, constrained in the same way.
UI for other functions – I can select plants by clicking on them, and then either replace them, evolve them with the genetic algorithm button, or save and load them.
A very obvious next step for good tool design would be to use Unity Editor Scripting, perhaps with the UI Toolkit, to make this UI accessible through the Unity editor rather than through in-game UI.
Final Thoughts
For me, this project was a decent and fast starting point for getting into more PCG content. If you want to find out more about PCG, I’d definitely recommend checking out Kate’s blog here - http://www.galaxykate.com/blog/generator.html
Performance optimization was key for making sure what I’m generating can actually be used in a real game. I might be looking into 3D foliage generation next, so optimization and just learning about the most efficient ways to render 3D art in Unity will be essential for that.
After this, I worked on a tool for procedural narrative generation. Keep an eye out for an article about that one soon!
Read more about:
Featured BlogsAbout the Author
You May Also Like