The most efficient way to display heavy environments

I’ve been working on systems for large environments since couple years, a lot mentioned in my project Tesseract, a realscale planetary engine for open world games.

More specifically for my mmo game i use the engine for, where you also can build houses like in The Sims, and from there towns and cities together.

Batch, Merge, Instance, Index, Cull, LOD

Asides of the terrain/world engine that is system for itself, the major boosters are the IndexedVolume - mass update optimized spatial index with hierarchical culling and chunked world content streaming, auto-instancing, auto-batching (merging geometries, textures and materials while preserving uv repeats) and the volume hull impostor (super low memory cost volumetric universal impostors) as one of the key components. But also a system for road, wall and fence networks which also utilizes the impostor and auto-instancing system, and a as well performance oriented particle engine with a static GPU driven component. Auto-instancing also goes as far as appending parameters, for instance some items in my game can be recolored like furniture, the auto-instancer turns instanced uniforms into an attribute.

The only thing i’m not sure of the benefit yet is occlusion culling as it comes with it’s own cost, but i probably will look into making use of it in a compound manner rather than individual items. Portals for interior for instance is a more smart (but specific) approach rather than the pure muscle strength approaches.

2D or 3D Impostor?

Any type of sprite impostor obviously can help a lot, but all 2D approaches suffer either from heavy popping or memory cost, that’s why i solved it in a universal approach (the VHI component) with fixed low cost and visual 3D appearance which also makes higher res volumetric impostors cheap in memory. But asides of the memory concerns the visual appearance was the most important, if you have a dense forest of trees the popping just becomes extremely noticeable with regular impostors.

Cardboard impostors also can be very good though, as they consist of multiple planes they don’t pop, and are commonly used for bushes, grass etc anyway. However they are very asset specific, and the transition not as good as a volumetric.

On the right when the origin line is visible the volume hull impostor is rendered, the transition from impostor to original and the visual appearance from angles is what bothered me most with sprite or cardboard impostors.

Here used together with the IndexedVolume and auto instancing.

Throttle

Something that can be done also without index, but getting the most out with, is throttling anything possible in the scene. For instance i heavily reduce animations on distance, also in skeletons skipping hierarchy such as not updating every finger anymore etc, also particle systems and anything that can be visually reduced or just skipped entirely.

Furthermore i also throttle shadows of CSM, every cascade after the first only renders every nth-frame with higher delay on higher cascade, visually it’s not heavy as it’s far away and most times not visible due the next frame being rendered already when the next pixel in that distance will be filled. It also means there are not 3 cascades rendered every frame anymore but only 1 to 2. Depending on mechanics i enable update of all like for the rare reason of fast forward daytime/sun.

Shadow rendering also goes through the IndexedVolume with all it’s features for regular rendering like instancing, but also a predefined shadow geometry can be provided by the asset to use instead the original, many games use this as the shadow often can go with a much lower detailed geometry. Regular billboard impostors for trees also can work very well even as single LOD. Shadows mostly need just silhouettes.

Batching instancing

A main concept of the spatial index is to touch and compute as little as necessary per frame, while the auto-impostor does actually cull and works with LOD versions and impostor, on high level nodes of the index a chunked baked compound is maintained in order to not touch any single object anymore, however while this concept ensure a maxed out performance for rendering rich landscapes it’s also more a fit for impostors as these can work with a single original quad or cube geometry and atlases for the different assets, otherwise the memory cost and rendering cost become a bottleneck again.

Separating logic

The index also has layers of roots, as the GPU accelerated procedural content decorator of Tesseract controls the vast foliage, rocks, plants, grass etc.it is separated from regular dynamic content as the decorator nodes are always only covering area cells that need to insert several ten to hundred thousand of assets and especially detach all of them again which is more efficient when separated from dynamic content.

Generally it makes sense to use multiple and different kind of indexes instead trying to have one for all kind of content, the road network system for instance has a significant different shape of elements than typical assets in an index. I also make use of subtrees, such as every lot of a house maintains it’s own node index tree while the lot bounding box is the root and also a leaf object in the main index.

Better performance without complex systems

For those working without any systems for optimizations the StaticMesh component i made a while ago might help a bit further, it works out of box, reducing computations massively and adding a concept of hierarchical compound culling for children of a scene root object, for instance all interior objects such as furniture of a house would be skipped entirely if the house itself was already culled.

Asides, obviously using InstancedMesh will help a lot in many suited cases, however many assume it works like regular meshes in the scene, just faster since it’s 1 drawcall, but it is static and has no culling of objects/instances, you will still likely always have a better performance with it for a lot of the same asset by how costly drawcalls and internal processing for one is. And generally ideally having a single mesh and textures for one asset, unless parts of it need to move like a door and it’s frame being separate.

6 Likes