Visibility and Occlusion Culling

Unreal Engine 4 (UE4) provides several culling methods of visibility and occlusion that are useful for optimizing game performance. Each method works to reduce the number of visible Actors in the Level by setting whether they should be drawn to the screen or not. Some of these methods (such as View Frustum and Hardware Occlusion Queries) can work simultaneously together or are better suited to specific devices and platforms (such as Round Robin Occlusion for virtual reality).

How Does Culling Work?

To get a sense of what Unreal Engine offers by default without any setup, we’ll look specifically at View Frustum culling and Hardware Occlusion Queries.

The general idea of visibility and occlusion culling methods is to reduce the number of visible objects at any given time with the goal of gaining performance.

If we start with only what the camera sees from it’s perspective, we see a handful of objects (left). However, we may know that to not actually be the case because we’ve placed a lot of objects around the scene that just aren’t visible in this particular view (right).

Scene View
Top Down Scene View
Given the scene view, we know that there are a lot of objects outside of the field of view (or view frustum) that aren’t visible and can thus be culled (red).

Scene View

Scene View with | View Frustum Culled Objects Removed

Objects outside of the view frustum are no longer rendered leaving only a handful of objects within the scene view that are occluded by another object. During this pass, queries are issued to the GPU testing each object’s visibility. Those that are occluded by another are not rendered (blue).

Scene View with | only objects in the View Frustum

Occluded Objects within | the View Frustum Culled

All objects that are either outside of the view frustum or that are occluded have been culled from view. The final scene view more closely matches the objects we know to be visible in the scene.

Vis_FinalSceneView.png

Unreal Engine 4 provides a number of culling methods, each with their own advantages and disadvantages where some are available for specific platforms.

For additional information, see Culling Methods below.

Testing Visibility with an Actor's Bounds

Each Actor placed in a Level has a bounding area that is used for various things in the Engine, one of those being specifically to test whether an it is visible or not. The bounds of an Actor is made up of two parts: a sphere and a box. The bounding sphere is used for fast collision detection with a simple distance test, and, more often than not, it's greater in size than the object it contains. On the other hand, the bounding box is a closer match to the shape of the object and provides more accurate results. 

Visualize bounds of Actors in the Level Viewport with Show > Advanced > Bounds. Alternatively, in the Skeletal Mesh Editor you can select Character > Bounds and in the Static Mesh Editor, select Bounds in the main toolbar.

ActorBounds.png

An Actor with it's bounds (sphere and box) displayed.

Each Static Mesh and Skeletal Mesh has its own bounding box and sphere that automatically scales to the size of the geometry when imported, or when scaled or rotated in the viewport.

You can edit the bounds of an Actor in a couple of ways:
  • By setting the Bounds Scale via the Details panel when selecting an Actor in a Level or Blueprint. The Bounds Scale uniformly scales the bounds of an Actor.
    Click image for full size.
  • By opening the Static Mesh or Skeletal Mesh Editors to apply non-uniform scaling for Positive Bounds Extension and Negative Bounds Extensions using the Details panel. 
    ActorEditorBoundsExtension.png
Increasing the bounds of an Actor can affect performance and shadow quality by keeping it rendered longer than needed.

Culling Methods

A culling method is used to track the visibility state of each Actor in your level. The scene’s data is culled and tested against whichever method(s) are being employed by your project.

The following culling methods are applied in this order based on their cost:

Unreal Engine uses View Frustum culling and Hardware Occlusion Queries (Dynamic Occlusion) by default for any project. If your project has a lot of Actor’s this can come at a cost to GPU performance, especially in instances where there are a lot of Actors visible within the scene view.

Distance

Distance culling methods, like per-Actor settings and Cull Distance Volumes, set distances from the camera that an Actor should or should not be rendered.

Per-Actor Instance

Each Actor in the level has its own settings for draw distance that can be set using the Details panel. There, you can set a minimum and maximum draw distance from the camera (in Unreal Units) for which this Actor should be rendered.

PerActorDistanceCullingSettings.png

Set the following with the Actor’s draw distance settings:
  • The Minimum Draw Distance an Actor should be visible from the camera. It is the closest you can get to an Actor before it is no longer rendered.
  • The Maximum Draw Distance an Actor should be visible from the camera. It is the farthest you can get from an Actor before it is no longer rendered.
  • And you can view (but not edit) the Current Maximum Draw Distance. It displays the stored cull distance set by a Cull Distance Volume, if one exists.

Cull Distance Volumes

Cull Distance Volumes use an array of distances and sizes to set whether an Actor is rendered or not when within the volume. This culling method is ideal for large outdoor levels that have detailed interiors where you want to remove objects occluded by larger ones at farther distances.
For additional information, see Cull Distance Volumes.

View Frustum

View Frustum culling uses the visible screen area of the camera’s field of view (FOV) to cull objects not within this space. The view frustum is a pyramidal shape that includes a near and far clipping plane which defines the closest and farthest any object should be visible within this space, all other objects are removed to save processing time.

ViewFrustumDiagram.png

  • The Near Clipping Plane is the closest point to the camera that objects will be visible.
  • The Camera Frustum is the pyramidal shaped representation of the visible viewing area between the near and far clip planes.
  • The Far Clipping Plane is the farthest point from the camera that objects will be visible.
Visualize the view frustum while editing in the Level Viewport by going to  Show > Advanced and enable Camera Frustum.

ViewFrustumSceneCameraFrustum.png

Camera frustum visualization enabled.
For additional information, see the How does culling work? section above.

Precomputed Visibility

Precomputed Visibility Volumes store the visibility state of non-movable Actors in cells placed above shadow casting surfaces. This culling method generates visibility data offline (during a lighting build) and works best for small to medium-sized levels. Precomputed visibility is ideal for lower-end hardware and mobile devices where the trading rendering thread costs for runtime memory gives the most gain.
For additional information, see Precomputed Visibility Volumes.

Dynamic Occlusion

The dynamic occlusion system in UE4 comes with several culling methods to choose from. Each of these methods track the visibility states of Actors in a level within the camera’s view frustum (or field of view) that are occluded by another Actor. Checking the visibility state of each Actor to know if it’s visible or not is done by issuing queries to the GPU or CPU (depending on the method being employed). A heuristic is used to reduce the number of visibility checks needed, in turn, increasing overall culling effectiveness and performance.

Unreal Engine uses the scene depth buffer when issuing occlusion queries. It enables longer view distances since it relies on approximate distances rather than a maximum draw distance (or far clipping plane). Using the depth buffer enables Actors which are movable or non-movable to occlude or be occluded by another Actor, including those using materials with opaque or masked blend modes.

Hardware Occlusion Queries

The primary dynamic occlusion method employed by UE4 is Hardware Occlusion Queries which issues Actor visibility tests to the GPU each frame as a query. The Actor’s visibility is read back one frame later—which can sometimes have the adverse effect of causing them to “pop” in if the camera is moving very fast. The cost of hardware occlusion scales with the number of queries performed on the GPU. Use Distance and Precomputed Visibility methods to reduce the number of queries performed each frame by the GPU. 

Hardware occlusion queries are enabled by default and will work on any platform that supports it, including some higher-end mobile devices that support ES 3.1 or higher on iOS and Vulkan on Android. Devices that do not support hardware occlusion queries can disable it from the Project Settings under Rendering > Culling by unchecking Occlusion Culling or by setting the following console variable in a device config file:

          r.AllowOcclusionQueries=0

For additional information on the cost of dynamic occlusion, see Performance Statistics  below.
Hierarchical Z-Buffer Occlusion
Hierarchical Z-Buffer (HZB) occlusion is a more conservative method of culling that uses a Mip mapped version of the Scene Depth render target to check an Actor’s bounds, and requires fewer texture fetches when samplings from a lower Mip. However, rendering the Scene Depth at a lower resolution can cause Actors to remain visible that should otherwise not be. 

HZB Occlusion can be enabled by using the following console command:

         r.HZBOcclusion=1

Software Occlusion Queries for Mobile

Software Occlusion Queries is an occlusion method that uses designated level of details (LOD) of an Actor to occlude ones behind it. This culling method rasterizes the scene on the CPU to cull Actors, where in comparison, Hardware Occlusion Queries perform visibility checks on the GPU. The conservative design of software occlusion means that it can be enabled and used on any mobile device.
For additional information, see Software Occlusion Queries for Mobile.

Round Robin Occlusion for VR

Round Robin Occlusion is one of the dynamic occlusion query methods to improve stereo rendering performance by alternating between one eye each frame instead of both. There is an added frame of latency to occlusion data which could result in incorrect rendering in the the periphery with the trade off being that Round Robin Occlusion saves an entire frame’s worth of queries. The trade off in savings can help most with scenes that are draw call or visibility bound.

Enable Round Robine Occlusion in your .ini config file or at runtime using the following console variable:

         vr.RoundRobinOcclusion=1

Performance Statistics

No matter the size of your game, one of the most important development passes to do is performance optimization. Culling objects from your scene is a good way to go about doing that. The statistics window enables you to look at various stats reflective of how visibility and occlusion culling are performing. For example, for Precomputed Visibility, you would want to keep an eye on how much memory is being used at runtime to load culled data or for Hardware Occlusion Queries you’d want to check how many primitives are being sent to the GPU during a given frame if you’re experiencing some hitching or sluggish FPS.

Call the stats window by entering stat initviews into the console window.

StatInitviews.png

Scene stats taken from the Infinity Blade Grasslands that is available on the Epic Games Marketplace.
The stats displayed are broken up into two sections: Cycle Counters and Counters. The Cycle Counter stats focus on the number of rendering cycles in milliseconds (ms) that it took to process primitives. There you’ll focus on the View Visibility, Occlusion Cull, View Relevance, and Frustum Cull. The Counter stats add all the primitives that have been processed in the current view.

Keep the following in mind when testing visibility and occlusion culling:
  • Occlusion culling is disabled in Wireframe view mode.
  • Use the hotkey G to switch to gamemode view while working in the viewport to see some culling methods, like Cull Distance Volumes or Precomputed Visibility Volumes.
  • For the most accurate results, use Stat InitViews statistics when in Play-In-Editor (PIE) or Standalone Game. Geometry for the Actors that represent Lights, Cameras, and others will be included when calculating visibility and occlusion culling results.
  • Keep an eye on Visible Static Mesh Elements because it is the single biggest contributor to rendering thread time and should be carefully watched and optimized.

Stat Name

Description

Cycle Counters
View Visibility The amount of frame time spent to process Actor visibility queries.
Occlusion Cull The amount of frame time spent to query the scene for Actors within the view frustum that were occluded by other Actors.
Frustum Cull The amount of frame time spent to query if an Actor's bounds were within the view frustum.
Decompress Occlusion The amount of frame time it took to load the precomputed visibility.
Counters
Processed Primitives The total number of Actors that are being processed in the scene.
Frustum Culled Primitives The number of primitives culled when not within the view frustum FOV.
Occluded Primitives The number of Actors that are occluded by other Actors from within the view frustum FOV.
Occlusion Queries The number of scene occlusion queries sent to the GPU based on the number of Actors visible within the view frustum.
Visible Dynamic Primitives The number of dynamic primitives in the scene that have their mobility set to Movable, like Particle Systems and Foliage instances.
Visible Static Mesh Primitives The number of visible meshes that have their mobility set to Static within the scene.

Debugging Culling

Below are debugging options you can use for visibility and occlusion culling. 

Visualizing Occluded Actors

You can use a visualization command while working in the Editor to check if an Actor is occluded.

     r.VisualizeOccludedPrimitives 1

When enabled, a green bounding box is drawn around any occluded Actor. 

If you have a lot of Actors in your Level, this visualization may not be a useful debugging method without hiding some parts of your Level and their contained Actors.

Visualized Occluded Actors

Hidden Geometry to | show occluded Actors

In this example, note that only Actors fully occluded by the walls and doors are occluded. The ones on the right visible through the hole in the wall are not fully occluded.

Freezing Rendering of the Scene

You can “freeze” the rendering state for Actors in your Level while working in the Editor so that you can freely move around within the Level viewport and inspect occlusion results.

Enter the command FreezeRendering from the view you want, like the example below.

FreezeRendering.png

Once entered, freely move around the scene to see the occlusion result. When moving the view to be behind the wall, objects fully occluded are not rendered and the Actors that were not fully occluded still are.

Normal Scene View

Freeze Rendering from | original camera view

Use the following commands to freeze rendering states for other types of Actors:

Console Command

Description

FreezeRendering Pauses / Unpauses the current rendering state of occluded and visible Actors in the Level based on the camera view.
Foliage.Freeze Pauses the current rendering state of occluded and visible painted foliage clusters in Level based on the camera view.
Foliage.Unfreeze Unpauses the rendering state of occluded and visible painted foliage clusters in the Level.
FX.FreezeParticleSimulation Pauses / Unpauses any CPU sprite particle simulations in the Level.
FX.FreezeGPUSimulation Pauses / Unpauses any GPU sprite particle simulations in the Level.

Using Game View Mode

Use Game View mode to get a sense of what the game will look like while working in the Editor without requiring you to use Play-in-Editor (PIE) or launch the game.

Enable Game View using the keyboard shortcut G or use the viewport dropdown menu and select Game View while working in the Editor.

Editor View

Game View

When Game View is enabled, Actor icons are hidden (like they would be in-game), such as those for Lights and Particle Systems. If you're using Cull Distance Volumes or Precomputed Visibility Volumes (and are in the cell), you'll immediately see the culling results of those volumes depending on their culling aggressiveness.