Tutorial
Heading
Mar 10, 2021

Optimizing VRChat worlds: static batching

This guide emphasizes one of the best practices in developing videogame environments: static batching

Premise

VRChat is a well-known social platform for those with VR equipment. Even if you don't have specific hardware, you can still join the party from your standard PC screen! When creating a scenario, caution must be excercised with texture sizing and data management to ensure both your scene and the target audience can run smoothly. Failing to democratize hardware requirements means failing to create a popular VRChat World.

The mission

This guide emphasizes one of the best practices in developing videogame environments: static batching.

We will delve into the value of static GameObjects and explore the mainly differences among static options. Finally, we will check the Avatar Garden (100 Avatars world) to provide you with a refderence point on how it is set in the scene.

Resources

  • Unity Editor (2018.4.20f1)

*Please note that this content is VRChat-focused but addresses a Unity Engine-related issue.

At the time of writing this article, VRChat SDK3 UDON system was not available. The content is mainly tailored for VRChat SDK2 and Unity General Knowledge.*

What is the static value?

A GameObject is considered static if it does not move at runtime. Conversely, if a GameObject undergoes movement during runtime, it is classified as dynamic.

Static GameObject menu

The static bool (or dropdown) is the method by which we instruct the engine to pre-compute calculations for the GameObject, keeping it stationary. This allows Unity to save on runtime processing, focusing on these calculations once and then shifting attention to dynamic objects with calculations in each frame.

By toggling its checkbox, all available static types are automatically checked or unchecked. If the object has children attached, Unity will prompt you to decide whether to apply these values to the entire hierarchy.

Applying a specific static type to an object is as simple as clicking the dropdown arrow next to static and selecting your desired values.

Using static can significally impact Unity’s performance behavior — it’s a fundamental optimization technique for achieving smooth FPS. Proper use of static is the key to optimal performance.

Game view statistics

In the game view, a tab called "Stats" provides information on the number of batches processed per frame within the current FOV of the camera set in the scene. Without features like Lightbakes, Occlusion Culling, Batching Static, this count could increase significantly, as each feature requires a separate calculation per object.

This includes calculations for mesh visualization, material aplication (1 batch per shader pass), lighting (1 batch per object for real-time lighting), and real-time effects like particles and skybox. Therefore, static-ing objects is crucial, especially for those indicating the engine not to modify anything from their render/transform—known as Static Batching.

Before delving into Static Batching, let’s explore different types and briefly explain their usage:

Static value:

  • Contribute to GI: influences the GameObject when computing Lighting Data.
  • Occluder Static: turns this GameObject a Static Occluder.
  • Occludee Static: turns this GameObect a Static Occludee.
  • Batching Static: marks this GameObject to merge its mesh/meshes with other GameObjects set to Batching Static.
  • Navigation Static: consider this object when computing Navigation Data.
  • Off Mesh Link Generation: attempts to generate an Off-Mesh Link (Navigation Data).
  • Reflection Probe: influences this object when rendering Reflection Probes.

For a comprehensive guide on creating an effective Occlusion Culling system in VRChat, refer to "Optimizing VRChat Worlds: Occlusion Culling.”

The static batching in the Gauguin Avatar Garden

Static Batching instructs the engine to combine similar meshes, reducing the rendering call count. In Unity, similarity is determied by checking Static Batching and having the same materials. Unity combines meshes based on materials, significantly reducing the rendering calls without compromising their independence as objects.

As an example from the Avatar Garden, objects with repeated assets across the environment but varying in transform values (while remaining consistent in material settings), are marked as Static. Unity efficiently renders these objects with matching materials, optimizing performance by rendering them only once.

  • Once for all the "orange mesh" GameObjects.
  • Once for all the "light-green mesh" GameObjects.

... and so forth for every material match among all the Static Batched GameObjects.

Static batching in Unity

The outcome of Static Batching in Unity resulted in the combination of all meshes useing the same water material and the merging of meshes using the ground material.

Water mesh on runtime
Ground mesh on runtime

If you animate a GameObject using its transforms and have Static Batching checked, it will not move during runtime. However, if you have an animation based on shader values. it will still be animated.

This implies that shader modification during runtime is still possible for Static Batched meshes.

Animation water mesh

The river in the Avatar Garden is an animation driven by the values of the Material Shader. The engine instructs the interpolation of two values over a specific duration through the Shader. This allows us to create such effects while keeping the draw call rate low.It’s important to note that during the animation, the shadow remains unaffected. This is atributed to the UV space used.

The standard shader uses UV0 for input texture maps like Albedo, Metallic and Smoothness. Consecuently, any values modified during runtime only affect the UV0 channel of the model. On the other hand, lightmaps use UV1, and these are not modified during runtime.

UV maps used in Unity

Reducing draw calls

Drawing a GameObject on the screen involves issuing a draw call to the graphics API (such as OpenGL or Direct3D). Draw calls can be resource-intensive, leadin to significant work on the CPU side for each call, causing performance overhead.

To mitigate this impact, Unity employs for dynamic batching or static batching, as mentioned earlier. Good practices to reduce draw calls include:

  • Combine different objects that share the same material.
  • Packing different independent objects into one larger atlas texture.

Texture packed
Asset using texture meshes

This serves as an illustration of atlas packing, where all exterior objects use the same texture maps and, consecuently, are drawn in a single call.

In the Avatar Garden, the implementation of tileable textures not only reduces the number of materials but also contributes to color depth and variation through lightbaked lighting.

Conclusion

The static value is imperative for enhancing runtime performance in Unity. Properly executed, it can significantly impact all machines, enabling individuals with low-end machines to run high-end experiences.

Feel free to ask any questions or share your experiments and results!

VRChat
Kourtin
Head of OPS

I purr when you're not looking. I'm passionate about environments and all the techie stuff to make them look rad. Learning and improving everyday to be a better hooman.

Tutorial
How to import Decentraland SkyboxEditor into Unity
Tutorial
Doing a MANA transaction in a Decentraland scene
Tutorial
Canvas (2D UI) Manager in Decentraland