• Register

Ardenfall is a open-ended single player game where you decide where to go, what to do, and how to do it. Become a thief of the night, a hero in shining glory, a puppeteer of politics - become a master of magic, or a powerful warrior. Explore a deep and alien world, talk to townsfolk, learn of the culture and the people who live in this fantasy world. Whatever path you choose, it is your own.

  • View media
  • View media
  • View media
  • View media
  • View media
  • View media
Post article RSS Articles

Our Foliage System in Unity

News

Foliage System

As everyone knows, Unity’s terrain / foliage system isn’t exactly modern. Hence, I’ve been doing everything I can to avoid the horrors of these systems. The first step in that direction was to build my own foliage system.

foliage

Cell Based Data

First off, a simple explanation of Ardenfall’s world-data structure is needed. In short, the world is split into Cells, some in a gridded form (like most of the world) but can also be on their own (a dungeon, for example). Each cell has its own Scene file, data file, and foliage file. (And of course, other data)

This allows for automated streaming (never load what you don’t need) and is also git-friendly – it’s now possible for multiple people to work on the same map, just by editing different cells. This data is automatically loaded and unloaded, making it easy for the foliage system to simply grab the current cells and do whatever it needs to do.

Foliage cell data is also split into subcells, just a simple array of classes. This is to make rendering faster and smarter.

Culling

Culling in Unity is incredibly easy. First, you need to calculate the frustrum planes:

frustrumPlanes = GeometryUtility.CalculateFrustumPlanes(camera);

And then when you want to see if a certain Bounds is inside the camera, just do a AABB check:

GeometryUtility.TestPlanesAABB(frustrumPlanes, bounds)

I utilize this by giving each foliage instance has its own bounding box, which is easily calculated by grabbing the bounds of the mesh. All these bounds add up to a sub cell’s bounding box, and the subcell’s bounding box adds up to a cell’s bounding box.

And so, when it’s time to render, it goes through all loaded cells and checks distance, and then checks culling for each cell. And then from within each subcell, it once again culls. And then in every instance, they’re culled again. This results in only culling what is needed. In other words, if an entire cell that consists of thousands of trees is completely off screen, it is instantly discarded on the first step, very cheaply. I also implemented a per-foliage culling feature, but this seemed to not help, in fact the extra computation on the main thread that this feature created seemed to be worse than just rendering the entire subcell on the GPU.

However, when culling is done like this, a new problem arises: shadows! One solution is to always render shadows, but I've found that shadows seems to be much more computationally intensive than the mesh itself. My solution was to basically extend the bounds of the mesh for shadow rendering: so if these bounds are on camera, only render shadows... if not, render nothing.

GPU Instancing

Instancing in Unity is incredibly easy, it’s quite fantastic. All you need to do is run Graphics.DrawMeshInstanced, plugging in the mesh, submesh index, materal, matricies, and so on.

Note that the material used must support instancing. Don’t worry, most do – you just need to check that little box.

instancing


If you have multiple submeshes, don’t worry – just run it multiple times, with a different submesh index.

LODS

Lod calculation is quite easy – for each foliage asset (tree type) I have, I just have a list of LODS. And then, for each subcell, the system simply calculates the distance from the camera to said subcell, and uses that to pick what LOD to use. It then uses Graphics.DrawMeshInstanced to render this LOD mesh. In other words, a LOD isn’t applied per-instance, but per-subcell. This results in faster render times (since the number of distance checks are kept at a minimum), with very little difference to the outcome. The only thing to be wary about is the subcell/cell size – too big is unnecessary, and too small and you will lose the “exactness” of the LOD distances.

Shadows

Shadows are quite expensive, but I’ve found they are so fantastically beautiful I just *had* to make it so as many people could play the game with fancy shadows. I haven’t done much yet, but I have added a simple shadow imposter system.

A shadow imposter is basically a simpler mesh that is used to only render shadows, while the actual visible mesh does not render any shadows. This results in a much more efficient render, as often complexities in meshes are unneeded when rendering shadows anyways. Trees are no exception. I’ve found I can create some nice shadows with only 1/4th of the number of verts:

lods


Of course, it’s not perfect – this was mostly a test to see how far I could take it, and it especially lacks leaves in the top, so any shadows casted onto other tree’s often look glitchy. Regardless, it works for now and is speedy fast.

Collision

Collision is automatically generated as the player walks through the game. The generation is run every second, but of course this can be modified. How collision works is quite simple – collider sizes are saved per instance, and then as the player walks through the world, colliders are created or destroyed. There’s a simple pool system implemented, since destroying and creating a bunch of gameobjects all the time is unnecessary.

This works especially well with my game, which has very little physics, and since NPC’s use navmeshes, collision is pretty much only used for the player.

Currently the navmesh does not generate with the collision trees, I still need to figure out how to design that. One simple way would be to create all collision objects during edit mode before navmesh creation, although this seems quite ridiculous and expensive.

Grass

What would a foliage system be without grass? Not a foliage system, I suppose. Grass is rendered using a mesh of vertices that are used as grass positions. This results in incredibly fast rendering, and very pretty results. However, currently my grass shader hasn’t been set up for deferred lighting (aka “infinite” number of light sources), so it’s quite glitchy.

I plan on writing an article on the grass when I get that working. Perhaps I’ll release the shader while I’m at it.

grass
Simple grass, ignore the glitchy shader


Future Ideas

Here are some ideas and thoughts on the future of this system:

Occlusion Culling: Currently a cell, subcell, or instance is only culled when it is offscreen. But what if it’s hidden by other objects, such as terrain or a wall? This is where occlusion culling comes in. Unity has this feature built in, and it looks like it’s somewhat easily accessible using their CullingGroup API. I’m not sure if my system gains much from this (how often will a subcell be hidden by other meshes?), but if so I am certainly interested.

Runtime Removal/Addition: Technically adding the function to add or remove foliage during runtime would be quite easy, since the data is quite accessible. However I’ll only look into this if I decide I need it.

Threaded rendering: The actual rendering of the meshes is handled on the GPU, but the system that figures out what cells / subcells to render is all run on the main thread. Using Unity 2018's new C# Job system may help with this!

Release of the Foliage System

I’ve thought about releasing this code, perhaps on github, however some key things need to be done before this is possible: mainly, making it so it doesn’t require my cell system. Also, there is a lot to be done – cleaning up code, making the GUI usable, and adding features. If I did update it, I cannot promise I’d keep it up to date. Regardless, it is a possibility in the future if enough people are interested.

Introducing Ardenfall

Introducing Ardenfall

News 4 comments

An introduction for the project, our goals, what has been done so far, and what will be done in the future.

Post a comment
Sign in or join with:

Only registered members can share their thoughts. So come on! Join the community today (totally free - or sign in with your social account on the right) and join in the conversation.

Follow Report Profile
Icon
Ardenfall
Platforms
Windows
Developer & Publisher
Team Arden
Engine
Unity
Contact
Send Message
Release date
Game watch
Follow
Share
Style
Genre
Role Playing
Theme
Fantasy
Players
Single Player
Project
Indie
Twitter

Latest tweets from @ardenfall

RT @joshcamas: Square up #indiedev #lowpoly #unity3d T.co

Sep 10 2018

RT @joshcamas: Added Skeletons to Ardenfall and it is the best thing #lowpoly #unity3d T.co

Sep 9 2018

RT @minionsart: Interactive grass using an array of object positions! #gamedev #unity3d #vfx Shader code + script for multiple obje… T.co

Aug 27 2018

RT @joshcamas: Added crouching, it's kinda nice ^_^ Also peep that new forest! #unity3d #ardenfall T.co

Aug 21 2018

RT @joshcamas: Snowy in Akaga #unity3d #ardenfall T.co

Jun 25 2018

RT @joshcamas: Fireballs are a necessity T.co

Jun 25 2018

RT @joshcamas: Rain! #unity3d #ardenfall T.co

Jun 22 2018

RT @joshcamas: Trying to design my ui to have as much info on one screen as possible, #morrowind style. It's certainly interesting… T.co

Jun 15 2018

Embed Buttons
Link to Ardenfall by selecting a button and using the embed code provided more...
Ardenfall
Statistics
Last Update
Watchers
10 members
Articles
2
You may also like
Blackreef Pirates
Blackreef Pirates Role Playing
Skyheart
Skyheart Role Playing
Moonfall
Moonfall Role Playing
Towards The Pantheon
Towards The Pantheon Role Playing
Deepfall Dungeon
Deepfall Dungeon Role Playing