How to Occlusion Culling properly

Hi Everyone!

We’ve been getting into performance and found out that one of the key optimization we can do is reducing draw calls.

Reading into the documentation, we found that Babylon does provide occlusion queries.
As I understood it, occlusion queries help us know if a mesh is occluded or not. But does it also automatically hide the mesh that is occluded (therefore reducing draw calls)? If not, then what would be the proper way to hide meshes?

I’ve created a playground where there are 2 meshes. One that is high on draw calls and another to as hide it.

Any help is greatly appreciated!

Having trouble to run your code, the download fails from dropbox with a 500 error :frowning:

Yes, if the occlusion query concludes that the mesh is occluded then it is not displayed.

See Occlusion Queries | Babylon.js Documentation for more info and a working PG.

Important thing: the meshes that must be occlusion queried must be displayed after the meshes that can (potentially) occlude them! The PG in the doc is working because the wall is created before the sphere, so will be displayed before it. In your PG, you should load the wall before the buggy for it to work (or set a renderingGroupId on the buggy meshes which is greater than the renderingGroupId of the wall).

In your case, your buggy is made of a lot of small meshes (250+), so creating an occlusion query for each one will be sub-optimal. I think you should instead create a fake mesh and set its bounding info = bounding info of the buggy and apply the occlusion query on this mesh alone. If the mesh is occluded, call setEnabled(false) on the Buggy root node. Something like:

In this PG, the clear color is green when the buggy is hidden, blue else.

3 Likes

Yes, it happens sometimes. If you keep running / reloading the page, once in a while it does work.

1 Like

It does happen from time to time but then still loads the object. I’m also a bit unsure why it does.

@Evgeni_Popov that was really helpful!

A side follow up regarding what you mention. If in case the wall and the buggy is in one glb file, what would be then the way to do it?

You can use the Mesh.renderingGroupId: set it to 1 for all meshes that must be occlusion queried. As the default value is 0, all other meshes will be displayed before them. Also, don’t forget to call scene.setRenderingAutoClearDepthStencil(1, false, false, false) so that the zbuffer is not cleared between the rendering groups.

2 Likes

Just to clarify I can set all 250 meshes occlusion queries as long as I set their renderingGroupId to 1 correct?

Also, I’ve tried with using the mesh (boundingbox) to set occlusion queries option. But it seems when I go inside a bounding mesh, It is being counted as occluded. (Will try to create a playground) An example of this is If I have a mesh of a room, it would seem that being inside the room counts as occluded therefore hiding the whole mesh.

Yes, you can set an occlusion query on each mesh if you want, they just have to be rendered after the potential occluders.

I’m not sure I understand fully your use case, a PG would help indeed.

1 Like

Yeah basically one of the use case is I should be able to load any model (that can have one or more meshes) and through occlusion culling, only see what is in front of me (In case I’m looking at a wall, it should hide it/not make any draw calls). But then as you mention the tricky part is to determine and make sure that they should be the last one to be rendered. I’ll try to first implement based on our discussion (occluders first, before the occluded mesh) and get back to you :slight_smile:

1 Like

@Evgeni_Popov I tried applying the suggestion you provided (making a fake mesh and applying occlusion query on it) but it seemed that it resulted to look like this:

There seems to be a box in the middle of meshes. I didn’t do any changes (other than setting the position of the fake mesh) but I’m also uncertain which change cause the box to appear. I tried setting the fake meshes visibility to 0 and also its material’s alpha to 0 but that seems to hide the meshes as well.

Would you happen to know if it is possible to remove/hide this sort of box and still have the occlusion query applied to it?

Have you set boxMat.disableColorWrite = true;? Also, you should set boxMat.disableDepthWrite = true; so that the invisible box does not write to the zbuffer.

1 Like

That did it! Thanks so much!

I have another problem (really sorry to keep having new ones) but I was able to make a playground of it. It seems that the occulsion doesn’t behave in this context. Would you happen to know why?
Link to the playground: https://playground.babylonjs.com/#MQBXQ1#9

It’s a timing problem: the “then” function for the buggy will be called after the one for the wall because the file is bigger, so the buggy will be created before the wall which is wrong: the buggy should be created after the wall so that it is rendered after. Or you can use the renderingGroupId property to overcome the problem:

1 Like

Odd when I tried to apply it, it now looks like this:

The car seems to occlude but the screen/wall does flicker quite uncontrollably.

That’s hard to know without a repro…

Make sure your dummy box is correctly positionned at the same location than the object(s) it represents.

I’ll try to recreate a playground of it on Monday(Will take a look on the position but I just used the same position from the mesh itself). Thank you so much for the support! Have a nice weekend! :slight_smile:

@Evgeni_Popov
I was able to replicate it!. It would seem you are right that it might have something to do with the positions.
Here is the playground https://playground.babylonjs.com/#MQBXQ1#11
The reason why I added the position and the parent is because without it, it would look like this:


But it would seem setting the position of the dummy box to the exact the same value triggers it to flicker.

The PG you linked is the one with the buggy (and it does work for me).

My bad! Here is the updated link: https://playground.babylonjs.com/#MQBXQ1#14