Generating Optimized 2D Art in Unity: Procedural Generation for Stylized and Optimized 2D Foliage

Published February 03, 2022 by Dyonisian
Do you see issues with this article? Let us know.
Advertisement

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!

Last year, I was looking for a side project to work on that would ideally let me start learning about procedural generation in both an easy and interesting way, and could also be completed with about a month’s worth of work. That’s when I started looking into L Systems and procedural foliage generation.

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

No alt text provided for this image

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

No alt text provided for this image


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.

No alt text provided for this image

After adding smaller, slightly changed trees next to each one:

No alt text provided for this image

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:

No alt text provided for this image
No alt text provided for this image

After combining meshes:

Draw Calls reduced to around 33%, Batches reduced to 0.5% of the initial count.

No alt text provided for this image

CPU and GPU time almost halved, which can loosely be translated to mean that performance and frame rate will be 2x better.

No alt text provided for this image

Combined, unanimated

No alt text provided for this image
No alt text provided for this image

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:

  1. Isolate trees to their own layer and make sure the render to texture camera only sees that layer (Culling mask).
  2. Center the camera on the tree.
  3. Render what the camera sees to a texture.
  4. Apply this texture to a sprite.
  5. 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.

No alt text provided for this image
No alt text provided for this image

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.

No alt text provided for this image

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!

Cancel Save
1 Likes 1 Comments

Comments

freya6

Looks interesting

April 14, 2022 10:47 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!

Featured Tutorial

I was looking for a quick and easy PCG (Procedural Content Generation) Unity project last year so I started playing around with 2D foliage generation. Two things I found interesting about this project: Aesthetics: How can you go beyond the basics and achieve a look and feel that belongs in a 2D game? Optimization: You've generated something, now how can you make sure it's actually usable in a game?

Advertisement
Advertisement