[UE4]Occlusion Culling
keywords: UE4, Occlusion Culling, 遮挡剔除, 场景优化, Scene Optimization, Distance Culling, View frustum, Precomputed Visibility, Dynamic Occlusion
Documents
Visibility and Occlusion Culling
https://dev.epicgames.com/documentation/en-us/unreal-engine/visibility-and-occlusion-culling-in-unreal-engine
Understanding Culling Methods | Live Training | Inside Unreal
https://www.youtube.com/watch?v=6WtE3CoFMXU
Culling in UE4/UE5 (Precomputed Visibility Volumes, and Cull Distance Volumes) (Recommended)
https://www.chrismccole.com/blog/culling-in-ue4ue5
Software Occlusion Queries for Mobile - UnrealEngine Official Docs
Working principle of Occlusion Culling
Original: How does object occlusion and culling work in UE4?
https://answers.unrealengine.com/questions/312646/how-does-object-occlusion-and-culling-work-in-ue4.html
Unreal Engine 4 uses an automatic process for culling that uses Scene Depth and the bounds of an object.
When using the Wireframe viewmode, this is not a good method for testing if an object is occluded in UE4. You can use the (Editor only) console command r.visualizeOccludedPrimitives 1
to view the occluded objects. This will render a green bounds box for any objects that are occluded. Adjusting the bounds scale will increase the green bounding box and can cause the mesh to be rendered even when it’s not in view.
In the project settings you can disable Occlusion Culling completely if you need, but in most cases this is not needed.
There is an alternative method of occlusion in the engine that is not on by default. It’s less strict than the currently default method. You can enable this by using the console command r.HZBOcclusion 1
This uses an approximation with occlusion culling. It will occlude the mesh dependent more on size and bounds scale than strictly on bounds scale. This can be useful in some instances, but problematic in others where it would cause meshes to be rendered that you wouldn’t necessarily wan to be when hidden. This is largely why it’s not on by default at the moment.
Using the first console command above is the best solution right now for debugging what’s being occluded and what’s not for the time being.
Origin: Performance boost on UE4 games for Radeon users - guru3d
HZB Occlusion is a technique supposed to increase performance is a method of rasterizing certain portions of a scene into a depth buffer and then performing image-space occlusion queries on a hierarchical structure constructed from the depth buffer.
In any realistic 3D scene, there are things you can see and things that you can’t see. It sounds simple, but the application of this knowledge can have very beneficial effects on the performance of an application billed with rendering the 3D scene in real time.
Generally, we want to lower the computational cost of objects that do not contribute to the scene - those objects that are outside our field of view or are obscured by other objects. The Z-buffer is the last stage in the rendering pipeline where an object can be identified as “invisible”; in this case, the object in question is a fragment, and it can only be identified as invisible when the fragment’s depth value is “deeper” than something that has already been rendered at the same raster position.
The Z-buffer test is invoked for each and every fragment of a higher-level object being rendered - the polygonal representation of a “thing” in the scene, such as a person, a wall, a car, etc - and thus imposes a rather high computational cost of visibility determination, even with recent optimizations made to hardware z-buffer mechanisms.
Occlusion Culling switch
Global Switch(In Editor):
Project Settings -> Engine -> Rendering -> Culling -> Occlusion Culling
. It’s checked by default.
Local Switch(for single Primitive):
Details Panel of Mesh -> Rendering -> LOD -> Never Distance Cull
, false
by default.
This property in C++ is UPrimitiveComponent::bNeverDistanceCull
.
Mask material only in early Z-pass
Q: What’s mean by Mask material only in early Z-pass
?
A: Put the depth calculation of the mask material into Early Z-pass(utilize the hardware feature early depth testing
).
The requirements of Mask material only in early Z-pass
:
- All objects that write depth, such as masked objects and skeletal meshes, must be put into Early Z-pass.
- In scenes with many characters, the overall GPU cost may be high.
In order to reduce the calculation of depth pass (also called depth prepass, which is part of UE’s PrePass), UE4 will let the GPU calculate by itself (The depth is calculated in post z. Different platforms and different manufacturers have huge differences in the performance overhead of calculating depth.) for materials with unknown final depth (e.g. mask material), and depth calculation is likely to trigger alpha testing (the reason see warning follows), and alpha testing is the weakness of tile-based GPUs (e.g. mainstream mobile GPUs, the reason see: Early Z and Discard on PowerVR).
This is the reason why EarlyZPassOnlyMaterialMasking
was disabled by default: Alpha-tested shader in early z pass makes it slower and brings in some complications.
The most common scenario of this kind is large-area foliages with mask material: if materials contain parameterized Opacity Mask
, then the performance will be dropped down when EarlyZPassOnlyMaterialMasking
was enabled.
Furthermore, there is another problem: when calculating depth, the opaque material can only need early z (called PreZ in UE4) without pixel shader, while the mask material requires pixel shader to calculate, and it will increase the overdraw of pixel pass (is part of UE‘s BasePass) to increase.
So, it’s necessary to test whether Early Z has a net gain on performance in the different scenario, the console command for testing (needs to be restarted, runtime switching is not supported):
[/Script/Engine.RendererSettings]
; default is 3
r.EarlyZPass=2
; default is false
r.EarlyZPassOnlyMaterialMasking=True
The switches in settings:Project Settings -> Rendering -> Optimizations:
- Early Z-pass
- Mask material only in early Z-pass UE4.16 has been optimized: write the depth of the entire scene in Early Z-pass, then write the mask material into an opaque material in BasePass, and finally use EQUAL instead of LESS to execute DepthTest to avoid the depth calculation on pixel pass.
You can also check if occlusion culling works using console:
r.visualizeOccludedPrimitives 1
Which primitive will be forced to turn on post z (late z, do Discarding: stencil test, depth test) by the GPU to calculate the final depth (the calculation process is still in the pixel shader, and early z is turned off):
- Masked materials whose
Opacity Mask
has been parameterized. - Opaque materials whose
Pixel Depth Offset
has been parameterized.
For more details, see:
Why “Mask material only in early Z-pass"not default On for Early Z-pass?
Effects and mechanism of Mask Material only in Early Z-pass
A complete review of the rendering flow of Unreal Engine 4
Occlusion Culling in UE5
CPU based Occlusion Culling
UE5 discarded software occlusion culling (CPU-side) and use HZBO (Hierarchical Z-Buffer Occlusion, GPU-side) by default.
Port and extend the UE4 Occlusion Culling system to UE5 - LinkedIn
Software Occlusion Culling for UE5 - github
Issues
Occlusion Culling doesn’t work
Origin: Occlusion Culling doesn’t work
https://answers.unrealengine.com/questions/458450/occlusion-culling-doesnt-work.html
Dynamic occlusion is on by default and does a pretty good job when given the opportunity. Unless all your static meshes are all one single mesh then occlusion is working.
You can test this by using the console command r.visualizeoccludedprimitives 1
. This will create a green bounding box around the actors that are occluded. This will work only while working in the editor and not while in PIE/standalone game modes. Alternatively, you can use the console command freezerendering
to toggle the current rendering state. Move the camera to outside of the are where it’s suppose to occlude the actors behind it. Use the console command and then you’ll see Rendering Frozen
in the top left of the screen. You can now freely move the camera around the scene and it will have frozen/paused the actors at their current rendering state. So if they were occluded and not being rendered they will not be visible when you move the camera behind you walls that were occluding them.
This is a good method to start troubleshooting your occlusion issues. You can also use the console command Stat Initviews
while in PIE/Standalone game and look at the counters at the bottom. Follow the stat for Visibile Satic Mesh Elements. This number should rise and lower depending on the number of Static Meshes that are visible in the viewing area.
Lastly, for Precomputed Visibility Volumes, this is a static, offline, way of handling occlusion. There are some caveats to using it, but overall it can be used. Before investigating adding it to your game I would instead troubleshoot and narrow down the issues regarding your issue with dynamic occlusion before moving to something else.
There is some documentation that is being worked on for Visibility Culling, Precomputed Visibility Volumes, and Cull Distance Volumes that will be available hopefully in the not too distant future.
If your still having issues or something I said doesn’t make sense feel free to post back here.
六盘山上高峰,红旗漫卷西风。今日长缨在手,何时缚住苍龙? ----毛泽东《清平乐· 六盘山》