HLOD Implementation (SceneLOD)
- HLOD generation occurs at edit time (however, the framework doesn’t preclude having run-time generation)
- Only HLOD0 is generated and follows the traditional LOD chain instead of replacing it (e.g. HLOD0 comes after LOD2), which gives us benefits by allowing certain assumptions when generating texture atlases
- HLOD0 is a combined mesh of the maximum LOD of each of the individual meshes in the volume; It is not decimated
- Meshes are not internally cut along bounding volume boundaries and instead are included in only one node of the nodes that are straddled
- Performance could be better with a custom build of Unity with a native implementation of HLOD. However, since we are wanting this to work on standard Unity, a fast path evaluation of the bounding volume hierarchy (BVH) exists for the monotonic case of moving away from objects, which was used for the demo
The entire process includes three steps:
We partition the entire 3D space (or selected space) into a bounding volume hierarchy (BVH). The basic partition method is using an Octree with expansion or contraction happening as a result of added/removed meshes. The threshold for splitting nodes is based on renderer count, since those are correlated to draw calls. Other approaches could also be used, such as the total number of faces within a volume. A typical Octree contains elements at the leaf nodes only, but in our implementation we collect all renderers included in a volume and store those in each node as a convenience. An example of Octree subdivision
Once the BVH is in place, HLODs can then be generated for each node in the hierarchy. We currently use the coarsest LOD of each renderer and combine those, however, it is also possible to use LOD0 and decimate. Due to the constraint of using existing LODGroup components, a GameObject exists for each volume, and children volumes are located underneath. The GameObject will have a LODVolume component and an optional LODGroup component if there were any renderers in that volume. If objects are moved, deleted, or added, then it will trigger a regeneration of that part of the hierarchy all the way up to the root volume.
Before a camera renders, it needs to set the correct LODGroups to be used for rendering. An HLOD is used only if all the renderers within a specific volume of a leaf node are currently using their max LOD. Essentially, this HLOD is coming after pre-existing LOD chains for those renderers. Because only one LODGroup can manage a mesh at any time we have to disable the original LODGroups for those renderers. Moving up the hierarchy, if all children LODVolumes currently have their HLOD enabled, then those can be replaced with a combined HLOD up the hierarchy.