Brainstorm designing an occlusion culling system?

So not so long ago I learned that Roblox may or may not do occlusion culling.

For context:

Three-types-of-visibility-culling-techniques-1-View-Frustum-Culling-2-back-face

Here is occlusion culling with an image.
It’s basically just discarding objects that are hidden behind other objects and not rendering them.

Roblox does frustum and backface culling which are pretty much basic in nearly every modern game engine.
Roblox as far as I know however, does not cull hidden objects.

In most cases this is not an issue, Roblox has been doing fine for years without occlusion culling and it also has potential draw backs such as the added overhead for having to check visible objects every frame.

The big but,

for a game project I might want to design and play around with an experimental Lua implementation of a culling algorithm to see how much it affects performance and perhaps my project will run a lot better on old/low-end computers.

My current idea so far:

My current idea is to give models a smaller “visibility” hitbox and put them in chunks so I don’t have to cycle through the entire workspace.

Perform a simple raycast and it it does not reach the camera without intersecting it’s marked as “hidden” and I can make it invisible to improve performance.

Now… making the object SHOW UP again is a different challenge, because for that I would technically have to raycast every single frame for every single object.

So while graphical performance would increase, CPU performance would decrease for every hidden object that gets added to the list of hidden objects.

That’s not good.

My method for making objects appear again was to have an inflated volume/hitbox that the player’s camera has to look and raycast into for it to appear again.
But I wonder if that’s truly the most convenient way to do it, I feel like it might not.

Game engines actually implement a lot of these culling optimizations with buffers, textures and whatnot but Roblox ain’t some engine where you can do low-level programming so I have to think of a simpler, more naive approach that makes use of higher-level functions like the physics engine and raycasts or region checks.

It might actually not be worth it to implement a custom culling system but I honestly want to give it a shot.
It might actually prove to be very useful for games that are open-world with massive mountains or indoors and have many rooms full of objects and whatnot.

All your feedback, comments and thoughts will be greatly appreciated!

6 Likes

They are probably going to introduce culling, but if you want to play around with it, go ahead and do so. The only drawback is that Lua is much slower than C++, so it would be a lot slower.

In addition, I would like to see how you will handle it, so I will be watching this topic frequently.

3 Likes

After reading this post from 2014 I kind of doubt they’ll ever really consider adding it.

Besides that, I think I do have an idea for how this could work, at least for simple shapes like boxes and spheres.

1 Like

Last time I checked, Lua has gotten quite fast.
It’s not as fast as C++, not by a long shot.

But Lua on JIT can be almost as fast as C++, which is really impressive.
Roblox’ current Luau VM despite not using JIT at the moment, still gets pretty comparable performance to Lua on JIT, it’s fast.

If I had to guess/estimate, it’s somewhere between 50 - 60-ish % the speed of C++?
A good algorithm should perform well.

Also @BurningEuphoria.

Roblox has been using the “because mobile doesn’t support it” excuse for ages for not implementing features.

Roblox has no reason not to implement it for PC and console, which DO support it.
I don’t even make mobile games so I quite frankly do not care either if mobile doesn’t support something.

BUT before I go off-topic.
Please do share what implementations you have in mind! :slight_smile:

Why not detect if it’s visible on the camera from it’s corners, < probably fast?
and then if visible raycast all part’s corners from the camera? < needs optimising

  • We can’t check from the center because else users would report ‘parts dissapearing randomly from certain angles’
1 Like

I realized this might happen actually.
I think I will mostly use occlusion culling for rooms or things like trees and buildings hidden behind big mountains but still inside the render distance.

So here’s 2 potential problems with occlusion culling.

The 2nd problem miiight have a solution that works maybe more than half the time?
I’ve considered only counting the object as “occluded” if the raycast for instance, only hits the wall on one of it’s longest sides at an angle that is more than 45 degrees for instance.

Not perfect but might solve some edge cases like this.

I don’t know how the script would be for the 2nd solution, but yeah, it would also work (sometimes).

Scripting no one need to worry about, I can script about anything on Roblox.

The real challenge here though is finding the right algorithm / method to implement and preferably one that’s not computationally heavy.
I think I’ll most likely reserve occlusion culling for indoor areas and large objects outdoors though.

I might know a way to implement occlusion culling for procedurally generated rooms and dungeons but that would unfortunately limit the ways that I could use it.

True, my idea was using multiple raycasts from part corners (not middle), yours used only one raycast but its less precise since if you overthink it, you find out second solution doesn’t work.

Every method has it’s drawbacks, the corner method too unfortunately.
If the corners were hidden and part of the middle as seen in the image is visible, the game would still think it’s occluded.

Now technically I COULD solve this issue with even more raycasts but at that point the performance impact on the CPU starts to outweigh the performance gains on the GPU.

1 Like

I understood your problem, probably this might need a special type of casting that is like shapecasting but detects where it doesnt hit instead of where it hits. That’d be really useful acually, since it doesnt need infinite amount of raycasts.

I personally think that this is impossible to do feasibly, but you might want to take a look at Camera | Documentation - Roblox Creator Hub although this is bound to the same issues you listed.

The problem you are going to have with this approach is that you are brute forcing your way into seeing if an object is visible. It’d be more efficient to instead project each corner /vertex of your model to the camera. Roblox already does this and they are able to do it really fast as they go and do the projections on each instance in parallel on the GPU. You can’t do that and hence any effort to try and make it efficient will be sacrificed by your loss in performance. Here’s the thing, when it comes to optimizing games usually the CPU/GPU isn’t the bottleneck, but rather what it is is when new resources are introduced into the scene. The introduction of new information/memory slows down the computer program. Given that, a way to optimize that is to simply reduce the number of parts you have. You could do that by having an occlusion culling system as you said, but honestly if it’s just one instance at a time it wouldn’t be worth it.

By the way, there’s a reason why roblox doesn’t just occlude those objects when rendering, it’s cause if they didn’t do that they’d have to use a much more computationally expensive way to render stuff. It’s just a principle with rasterizers that you need to render each polygon at least as a collection of lines, see if they get occluded, and then “render” then in the sense that we think of rendering as plotting pixels on the screen.

EDIT: if you want to just have x amount of trees in the scene, you could create an algorithm that plots them on the screen and then if a tree is not visible by the camera, it repositions that tree to a new acceptable area defined by the area that’s visible on the screen. Though, at that point it’s probably better to take a different approach like a level of detail system where if you can see more than 50 trees, then the trees will get clumped into simplified instances so it gives the illusion of having a lot more trees

So the thing here is, the point of occlusion culling here is that I could make more complex scenes without having to reduce the detail.

Removing parts or using less models is generally the more “simple” and “naive” way of optimizing a game.
But, as you probably would have guessed, this sacrifices details and you essentially limit yourself with the amount of objects you could place down.

The whole point of occlusion culling here is so I can have MORE objects and detail.
I want to push the engine to the limits by having the absolute highest amount of objects as possible.

There is one project of mine where I want the player to be able to completely wreck up a room, defeat the enemies etc.
And I want ragdolls, bullet holes, destroyed objects and debris, etc to stay around forever until the level is unloaded so removing them is no option.

The issue with Roblox currently is though that if you’re inside a huge building for instance, EVERY ROOM within the frustum of the camera will get rendered regardless of whether it’s visible or not.

Every piece of furniture, all the meshes and parts it’s made out of.
Any NPCs, interactable objects, the walls themselves, the doors, the lights and shadows.

EVERYTHING gets rendered regardless of visibility.

Most developers here on the platform would just tell you “Just reduce the amount of parts and models you use, simple”.
Which yes, that does work, but it’s also very generic advice that almost anyone should know at this point.

But as simple as that sounds, removing objects from the scene and reducing complexity is not always what you want.
Sometimes complexity is intentional game design and it might be there for a reason.

In such cases I do not think simply reducing scene complexity is the go-to solution.
It would not achieve the results that I’d want for sure.

What if I WANT to render thousands of trees? What if I wanted a entire city with thousands of large buildings full of windows and whatnot and looooots of cars driving around?

So I have to come around with a work around such as implementing my own Lua software occlusion culling system.

Now, fortunately, I’m a little familiar with multi-threading.
So even IF the algorithm ends up being a little expensive, most computers (and phones) have about 4 cores, some 6 or 8.
And I think that’s enough for multi-threaded game logic.