Categories & Tags
Archive

Occlusion Culling in Unity 4.3: The Basics

December 2, 2013 in Technology by

The following blog post was written by Jasin Bushnaief of Umbra Software to explain the updates to occlusion culling in Unity Pro 4.3.

Unity 4.3 includes a plethora of improvements. One of the completely re-implemented subsystems is occlusion culling. Not only has the interface been simplified and the culling runtime itself revamped, a number of new features have also been added.

In this series of three posts, I’m going to go over how the new occlusion culling system works in Unity 4.3. This first post goes through the basics of how occlusion culling is done and what the basic usage is like with the user interface. The second post focuses on best practices to get the most out of occlusion culling. The third and final post focuses on some common problem scenarios and how to resolve them.

rsz_01_window_menuBut let’s start with some basics. Occlusion culling refers to eliminating all objects that are hidden behind other objects. This means that resources will not be wasted on hidden stuff, resulting in faster and better-looking games. In Unity, occlusion culling is performed by a middleware component called Umbra, developed by Umbra Software. The UI, from which Umbra is controlled in Unity, can be found in Window -> Occlusion Culling, under the Bake tab.

How Umbra Works

Umbra’s occlusion culling process can be roughly divided into two distinct stages. In the editor, Umbra processes the game scene so that visibility queries can be performed in the game runtime, in the player. So first, Umbra needs to take the game scene as its input and bake it into a lightweight data structure. During the bake, Umbra first voxelizes the scene, then groups the voxels into cells and combines these cells with portals. This data, in addition to a few other important bits is referred to as occlusion data in Unity.

In the runtime, Umbra then performs software portal rasterization into a depth buffer, against which object visibility can be tested. In practice, Unity gives Umbra a camera position, and Umbra gives back a list of visible objects. The visibility queries are always conservative, which means that false negatives are never returned. On the other hand, some objects may be deemed visible by Umbra even though in reality they appear not to be.
rsz_02_umbra_workflowIt’s important to realize that, while this system appears similar to what was shipped with previous Unity versions, the entire system has been basically rewritten. A lot has changed for the better, both internally and externally!

How to Use Umbra
There are obviously a few considerations in getting the best out of occlusion culling. Ideally, you’d want the least conservative result as fast as possible. There are, however, tradeoffs involved. The more accurate (i.e. the least conservative) results you want, the higher-resolution data you need to generate. However, higher-resolution data is slower to traverse in the runtime, yielding slower occlusion culling. If occlusion culling requires more frame time than it saves by culling, it obviously doesn’t make a whole lot of sense. On the other hand, very quick culling isn’t of much help if only a few objects are culled. So it’s a balancing act.

03_occlusion_uiThe way Umbra lets you control this balance is by having you define a couple of bake parameters. The parameters determine what type of input the bake process should expect and what type of data is generated. In the runtime, using Umbra is as simple as it gets. If you’ve baked occlusion data and the camera has occlusion culling enabled in the Inspector, Unity will use Umbra automatically.

Smallest Hole
The input is controlled using the smallest hole parameter. When voxelizing the occluder geometry, smallest hole maps almost directly to the voxel size. This means that if your geometry contains intentional holes, gaps or cracks that you wish to see through, using a smallest hole smaller than these is a good idea. On the other hand, a lot of the time the geometry contains lots of unintentional cracks that you do not wish to see through. A reasonable voxel resolution will patch these up. It may help to think about smallest hole as the “input resolution” of the bake.

Note that setting smallest hole into a ridiculously small value means that baking will be unacceptably slow and/or take up a monumental amount of memory in the editor. In some rare cases, it may even cause the bake to fail due to insufficient memory. Then again, while using a larger value will be faster and more memory-friendly, it may cause Umbra to not see through things like grates or fences. So bigger isn’t always better either. In general, a smallest hole as large as possible without visible errors is desirable. In practice, we’ve found that values between 5 cm to 50 cm work fairly well for most games where the scale is “human-like”. The default value in Unity is 25 cm, and it’s a good starting point.

rsz_04_smallest_hole
Smallest Occluder
While smallest hole mostly deals with what type of input geometry you have, smallest occluder determines what kind of output data is produced. In essence, you can think about smallest occluder as the output resolution of the data. The larger the value, the faster it is to perform occlusion culling in the runtime, but at the cost of increased conservativity (false positives). The smaller the value, the more accurate results are generated, but at the cost of more CPU time. Obviously higher-resolution data will mean a larger occlusion data size as well.

rsz_05_smallest_occluder

So as the name implies, a small value means that very fine features are captured in the occlusion data. Under the hood, this directly maps to how large cells Umbra creates. Lots of small cells mean lots of small portals between them, and naturally it’s more expensive to rasterize a large amount of small portals than vice versa.
The effects of changing smallest occluder can be seen in the picture below. Note how the depth buffer, which is essentially what Umbra sees, loses detail as smallest occluder increases.

rsz_06_level_of_occluder_detailIn most games, keeping smallest occluder slightly larger than the player, so around a few meters, is a good default. So anywhere between 2 and 6 meters may make sense if your game’s scale isn’t microscopic or galactic. The default value in Unity is 5 meters.

Backface threshold
Perhaps the most difficult parameter to grasp is called backface threshold. While in many cases you don’t really need to change it, there are some situations in which it may come in handy to understand how it affects the generated data.

First, it’s important to note that the parameter exists only for a single purpose: occlusion data size optimization. This means that if your occlusion data size is OK, you should probably just disregard backface threshold altogether. Second, the value is interpreted as a percentage, so a value of 90 means 90% and so on.

OK so what does backface threshold actually do then? Well, imagine a typical scene that consists mostly of solid objects. Furthermore, there may be a terrain mesh whose normal points upwards. Given such a scene, where do you want your camera to be? Well, certainly not underneath the terrain, that’s for sure. Also, you probably don’t want your camera to be inside solid objects either. (Your collision detection normally takes care of that.) These invalid locations are also ones from which you tend to “see” mostly back-facing triangles (although they may of course get backface-culled). So in many cases it’s safe to assume that any location in the scene, from which the camera sees a lot of back-facing triangles, is an “invalid” one, meaning that the in-game camera will never end up in those locations.

rsz_07_backfacing_geometry

The backface threshold parameter helps you take advantage of this fact. By defining a limit of how much back-facing geometry can be seen from any valid camera location, Umbra is able to strip away all locations from the data that exceed this threshold. How this works in practice is that Umbra will simply do random-sampling in all cells (see the previous post) by shooting out rays, then see how many of those rays hit back-facing triangles. If the threshold is exceeded, the cell can be dropped from the data. It’s important to note that only occluders contribute to the backface test, and the facing of occludees doesn’t bear any relevance to it. A value of 100 disables the backface test altogether.

So, if you define the backface threshold as 70, for instance, to Umbra this means that all locations in the scene, from which over 70% of the visible occluder geometry doesn’t face the camera, can be stripped away from the occlusion data, because the camera will never end up there in reality. There’s naturally no need to be able to perform occlusion culling correctly from underneath the terrain, for instance, as the camera won’t be there anyway. In some cases, this may yield pretty significant savings in data size.

It’s important to stress that stripping away these locations from the occlusion data means that occlusion culling is undefined in these locations. “Undefined”, in this context, means that the results may be correct, incorrect (pretty much random) or return an error. In the case of an error, all objects are simply frustum culled.

Of course in some cases, there just happens to be some amount of back-facing geometry in valid camera locations too. There may be a one-sided mesh that has been, possibly erroneously, tagged as an occluder. If it’s a large one, it may cause the backface test trigger in nearby areas, resulting in culling artifacts (=errors). This is why the default value of backface threshold in Unity is 100, meaning the feature is disabled by default.

rsz_08_backface_threshold

Feel free to experiment with the parameter. Try reducing the value to 90, which should drop a lot of data underneath terrains for example. See how it has any noticeable effect on the occlusion data size. You can go even lower if you want. Just remember to do so at your own risk. If you start popping into rendering artifacts, increase the value back to 100 and see if it fixes the problems.

To be continued…
In the next post, I’ll go into some best practices and recommendations for how to get optimal results out of occlusion culling. Please visit www.umbrasoftware.com for more information about Umbra.

Part II
Part III

rsz_09_umbra3_logo_v1_bgw_rgb

Share this post

Comments (29)

Comments are closed.

Adam
2 Dec 2013, 4:34 pm

I guess it should be noted that Occlusion Culling is a Unity 4.3 Pro feature and not available in the free license.

Kristyna Paskova
2 Dec 2013, 5:13 pm

Makes sense! I’ve edited the first paragraph. Thanks for pointing this out.

Bluestrike
2 Dec 2013, 5:40 pm

If I don’t use a backface treshold of 10 alot of my geometry is not rendered in this scene.
This is not supposed to be normal is it?

http://www.spheritis.com/tempspul/occluderissues.jpg

2 Dec 2013, 5:55 pm

its great feature !

2 Dec 2013, 7:02 pm

BLUESTRIKE: Sounds indeed a bit weird. Have you filed a bug report with a repro case? I’d love to take a look.

Richard Fine
2 Dec 2013, 7:57 pm

I remember reading on the Umbra site a while ago that it’s possible to point-query Umbra to find out ‘is point/box X visible or not’ – useful when e.g. trying to determine whether you can spawn an object at a particular position without the player seeing it pop in.

Can you tell us anything about why this isn’t exposed in the Unity API? Is it something that the Unity guys just haven’t got around to implementing yet and we might see in the future, or is there a deeper technological/business reason you can tell us about?

Dave Reed
2 Dec 2013, 8:09 pm

It’s good to see this information, it certainly seems a rather advanced system, but I’m rather concerned about performance on mobile devices – it seems rather heavy on the CPU at runtime.

I’m looking at the profiler for one of our scenes, on Android, and seeing culling taking as much time as drawing (around 20ms each), in cases where there’s not a huge amount being culled.

Are there any further tricks to boost performance on mobile? Is object count now more performance-critical? (we’ve got a fair number of small objects, and have relied on static batching)

Or are we going to have to go back to 4.2, for the PVS system?…

2 Dec 2013, 10:04 pm

I got the same issues as Dave reed, Culling (in this case Frustum Culling) is eating my CPUs alive. I’ve submited a bug a while ago with an example scene attached to it. Contact me via email if you need more information or help to solve this issue.

PS: I would like to request the same feature Richard is asking for. Could be very useful yeah.

3 Dec 2013, 11:59 am

@George and Dave, others: regarding performance, one of the best ways to optimize occlusion is using the simplest geometry possible- a 10 poly mesh will be much, much more efficient than a 1000 poly mesh. Obviously, you’re actual visual meshes can’t be just 10 polys though. That means creating special, optimized meshes just for occlusion- rather like collision meshes. That’s extra work of course, but can be crucial to a game that has serious visuals, in order to maintain good performance. If it isn’t too shameless, I would recommend looking into “ProBuilder”, which has specific methods to build and edit ultra-efficient occlusion-only meshes, right in Unity. Quick, simple, and makes a substantial difference, especially on mobile :)

3 Dec 2013, 1:04 pm

@Richard: Yes Umbra does have this functionality but we haven’t yet got around to exposing it in Unity. We can certainly put that on the TODO list though.

@Dave: 20ms is way too much, and is basically always a symptom of using an unnecessarily small a value for “smallest occluder”. Depending on the geometry at hand, Umbra may not be able to cull any better in some tricky spots regardless of how small a value you use. Typically spots where there are long lines of sight and little occlusion, the query is somewhat expensive due to large amount of portals that need to be traversed, and obviously in such spots not a whole lot can be culled, as there’s not much in the way of occlusion. Object counts do not come into play except for very, very extreme cases.

@Gabriel: With Umbra, this is basically not the case. Umbra specifically doesn’t care one bit about the triangle/vertex counts of the occluder meshes and thus they have no impact on runtime performance. This is made possible by the voxelization of occluders when baking. Umbra specifically allows you to use the real geometry for occlusion purposes rather than having to generate separate occluder meshes. Your toolset looks super cool though! :)

Bluestrike
3 Dec 2013, 2:41 pm

case:579155
Also perhaps this has a bit to do with it?
case 576650

4 Dec 2013, 2:04 am

@Jasin Bushnaief, do Umbra hide visible geometry based on occlusion/frustum or the other way around (eg display only the necessary data)?
I’m trying new values and kind of giving me better results, let’s see how it goes.

4 Dec 2013, 7:27 am

@Jasin- Ah, that’s beautiful! Hats off to you guys, in that case, for eliminating one of the major annoyances from Occlusion prep. Thanks for the clarification, and the compliment!

4 Dec 2013, 1:54 pm

@Georges: I’m sorry I don’t quite understand your question. Are you asking if Umbra reports some objects visible even though they may end up being hidden in the final rendered image? If so, the answer is yes. Umbra will always return some amount of extra visible objects and thus is always conservative. The amount of conservativity is mostly controlled by the “smallest occluder” parameter. (Higher value = more extra visible objects and vice versa.)

Richard Fine
4 Dec 2013, 6:08 pm

@Jasin: Good to hear :)

4 Dec 2013, 8:22 pm

@Jasin Bushnaief,
No sorry, I was basically asking if Umbra is the one in charge of displaying geometry in Unity? Or if it simply it provides view data for Unity to check which meshes must be seen or not?

5 Dec 2013, 2:28 pm

@Georges: Ah, so Umbra in itself doesn’t do any rendering of Unity’s meshes. Given a camera transformation, it simply returns a list of visible objects, which Unity then proceeds to process and render.

6 Dec 2013, 8:46 am

@JASIN,
Thanks for your clarification. Looking forward to try out new future Umbra features in Unity! :)

Bluestrike
8 Dec 2013, 11:47 am

@Jasin,
Did you take a look at the project I uploaded? (took me almost an entire work day to upload)

9 Dec 2013, 6:49 pm

@Bluestrike: I sent you an email.

13 Dec 2013, 2:17 am

@JASIN,
Thanks for your clarification

15 Dec 2013, 9:08 am

@Jason Basically why Unity doesn’t expose features like this. For example you could do a similar thing with PhysX and allow us to simulate objects one by one ourselves or use deltatimes for simulation other than fixedDeltaTime which can become handy in multiplayer games? Hope it’s not too out of topic.

Jan
20 Dec 2013, 9:13 am

Please update your Unity Manual. It still talks about Target Volume and containts outdated screens:
http://docs.unity3d.com/Documentation/Manual/OcclusionCulling.html

Jan
20 Dec 2013, 3:37 pm

Are dynamic objects (like an enemy) in an Occlusion Area (OA) which is occluded still culled or does the new version of Umbra only support static objects? Is this also the reason why “Is Target Volume” proptery of OA component is gone?

26 Dec 2013, 3:45 pm

Expecting some cool 2D line drawing APIs in it.

tobi
28 Dec 2013, 12:06 pm

this is so hard and i only 10 old and i like unity games

3 Jan 2014, 12:33 pm

@Ashkan: Not sure if I completely follow you, but Umbra isn’t exposed to users mostly due to performance reasons.

@Jan: With the new system, dynamic objects are automatically culled and the concept of target volume is now obsolete.

4 Jan 2014, 1:49 pm

@Jasin
thanks for your explanation

Garrett
27 Jan 2014, 6:06 pm

Does Umbra take advantage of multiple core CPUs?

Leave a Reply

Comments are closed.