Hey- does anyone have any tips on world optimization, and in general making it less laggy?

Just wanted to know. Its a bit hard finding up to date optimization tips.

There’s a lot you can do! Our docs have a page that’s a little bit misnamed-- that page contains a lot of tips for world optimization. Here’s some of the bigger points summed up.

Performance Stuff

  • Avoid real-time lighting, unless you really, really, really need it. If you do need it, ensure you use it sparingly.
  • As an addendum to the above, bake your lighting. Baking lighting is a somewhat challenging process, but it turns lighting from a “your computer has to calculate lighting every frame” operation into a “your computer looks up pre-computed lighting from a big table” operation, which is a lot cheaper.
  • Keep mirrors off by default. Use a button or something to turn them on.
  • Avoid using shaders that employ grab-passes or tessellation. One particularly bad culprit is that “rain-on-a-window” caustics shader-- it devours GPU time due to heavy grabpass usage. I’m on a 2080 and having 50% of my view obscured by that shader will drop me down by 20 frames. If you must have it, have a button that lets you turn it off.
  • Try not to use textures that are “too big”. It is up to you to determine how large that is, but the main concern is that you don’t want to eat up too much memory with massive textures. This is a particular concern on the Quest, where memory is at a premium.
    • As an aside, Crunch compression is great-- but Crunch compression does not help with in-memory size, at all. It only helps with the built size of the asset. The texture must be decompressed in memory to be used by the graphics card.

Not-Performance Stuff

There’s also some things you should do/ensure you’re doing to make sure your world looks good for everyone in it.

First off, please test your lighting against multiple shaders! There’s a lighting test avatar in Mimi’s Avatar Testing that lets you test a bunch of common avatar shaders. It pulls up a bunch of spheres with shaders on them on one of the gestures. Test your world in various spots using that little setup.

If anything looks overblown you need to adjust things. In particular, ensure that no single place in your world has more than 1.0 combined intensity from various lights.

I’ve seen way too many worlds use directional lights set to 10 intensity or something insane because the author tested the world using toon shaders that clamp lighting received to 0-1. This means anyone using a shader that properly respects lighting will look like an exploding star-- especially if you’re also using Bloom.

Speaking of Bloom, you should use tonemapping! Tonemapping (or color grading, or color correction, etc etc) is a great way to ensure your world looks good with color and light balance. Silent has a really good guide on post-processing that she keeps up to date. The values she has in there are her personal preferences, but they’re pretty good. The advice given in her guides are on-target, though. At minimum, you should always use tonemapping.


Another thing to add is Occlusion Culling. By default unity has frustum culling, anything that is outside of your view is not rendered. The problem with frustum culling is that anything that is behind a wall or hill with still render. Occlusion Culling bakes into the scene what should and shouldn’t be seen at each specific location. It may take some tinkering to make work, but for worlds with lot of line of sight blockers it is a huge performance boost. Occlusion culling when setup properly will even occlude dynamic objects like avatars and pickup when not in view.


Really good point! Do you know if there’s any guides out there for setting up occulusion culling for VRChat specifically? I haven’t seen too many.

1 Like

It’d be a pretty short guide, probably. :3 Just mark stuff as static (or specifically Occluder/Occludee static) and click “Bake” in the occlusion tab. The default settings rarely need adjusting.

Tangentially related to Occlusion though, combining meshes can also be useful. Say you have a little group of a dozen trees that are relatively low poly, you can use a mesh-combining plugin to turn these all into a single mesh (even with multiple materials).

That’ll either occlude all of them or none of them, but if they’re relatively low poly, the reduction in draw calls will probably more than likely make up for any extra verts that don’t get culled. I tend to only use it for things with same materials that are grouped closely together.

Another nice thing you can do is go into Play mode in Unity, then on the Game tab, click the “Stats” button at the top right. It’ll give you some info about how much “work” Unity is having to do. Of particular note is the Batches section.

You can try toggling off sections of the map to see how many batches certain objects are using. (they’ll go back to normal when you exit Play mode)

It’s a good way to see what’s eating up your performance and focus your efforts on the biggest offenders. If you pick enough low-hanging fruit, you’ll usually get to 90FPS pretty easily for most people.

If you’re super nerdy, check out the Profiler and Frame Debug windows. The Frame Debug is nice because if you step through the scene draw, you can see what specifically is breaking batching.

Just click Enable in frame debugger, then click something on the left. Look for something like:

Why this draw call can't be batched with the previous one
<some reason here>

This is a good read: https://software.intel.com/content/www/us/en/develop/articles/unity-optimization-guide-for-x86-android-part-3.html#

1 Like

Sorry for the late response, I was shown how to do it one on one. I also haven’t seen many tutorials on this subject. This is what I’ve learned about occlusion culling, eremite covered some of this already so some of this info will be restated.

  1. Occlusion culling needs to be baked, the tab re: occlusion culling can be accessed via the window menu tab.

  2. Objects need to be marked as occluder/occludee static to be calculated in the bake. Objects that move around a lot (like a pickup) should not be marked occluder/occludee static.

  3. If the total bake data is larger than 1mb than something is wrong, and the values need to be adjusted.

  4. You generally don’t need to adjust the default values, unless the above or below.

  5. You can test occlusion culling using the main camera and the visualization mode to see if it works properly.