LOD, real quick
Published 8 months ago
Add LOD nodes to your scene with practically just one script.
I've had a string of unexpected successes with Unity's LOD (Level of Detail) system. The API takes getting used to but you get negligible overhead even with tens of thousands of game objects. But the real issue with LOD is generating the geometry and setting up.
Unity's LOD system needs LOD groups with several mesh renderers assigned to each object. With large scenes this requires automation. I wrote a few and here's one I love since it only takes dragging a script onto an object hierarchy to enable LOD on all renderers. Depending on your scene structure this might work wonders - or at least help you decide whether you should invest more time.
Fuzzy LOD
The easiest approach to LOD is letting small objects disappear even before they exit the culling distance. This works well if your geometry consists in large masses, with smaller objects adding detail - often the case with vegetation, but would also work, say, if you had a house with a tiled roof (make a simple version of the house, and put the tiles in separate objects). No extra meshes involved comes with benefits:
  • You needn't call back your artist or invest into a decimation solution.
  • Scene files don't get larger - great for mobile where large scenes hurt downloads and loading time.
But say you have a forest seen in the distance - do you really want all vegetation to disappear at once? And do you not want a few random tiles rendered over that roof to keep a good feel (a time honored trick used by painters and illustrators)? This is where fuzzy LOD comes in play.
Here the culling threshold is randomized on a per object basis - in the example below, between 0 and 0.2 (specified as a percentage of viewport height). This causes some objects to disappear faster than others even though they're all about the same size.
Works with any scene, but for best results keep small objects separate.
Note: There is an urban legend going around that merging objects makes rendering faster. (Almost frustratingly) proven wrong in every situation I ever tested it.
The script
Here is the script used in the above demo. You needn't add the script to every object in your scene since LODs are setup recursively.
/* Created by TEA de Souza (eelstork) - public domain */ using UnityEngine; public class FuzzyLOD : MonoBehaviour { public float threshold = 0.25f; [Range(0.01f,3f)] public float power = 0.25f; public bool runtime = true; SerialF serial = new SerialF(); void Update(){ if(serial[threshold + power]) return; Update(transform); if(!runtime) Destroy(this); } void Update(Transform t){ SetupLOD(t); foreach(Transform x in t) Update(x); } void SetupLOD(Transform x){ var r = x.GetComponent<Renderer>(); if(!r) return; var group = x.GetComponent<LODGroup>(); if(!group) group = gameObject.AddComponent<LODGroup>(); LOD[] lods = new LOD[1]; lods[0] = new LOD(prnd*threshold, new Renderer[]{r}); group.SetLODs(lods); group.RecalculateBounds(); } float prnd{get{ return Mathf.Pow(Random.value, power); }} } public struct SerialF{ float value; public bool this[float x]{ get{ if(value!=x){ value = x; return false; } return true; } } }
  • Linear randomization doesn't give optimal results. So I combine the randomizer with a power function. If you set power to a low value, (0.25 a good place to start), more objects disappear earlier.
  • Tweaking settings at runtime makes adjustments easier and faster but once you're done - and if you don't want this in production mode - set runtime to false.
  • SerialF is a goofy but harmless structure to help checking whether settings have changed. Is there a programming hell for devs who abuse indexers in this way? I hope so!
Wishing you a Happy New Year!
Software Engineer - Programmer