Optimizing your VR/AR Experiences
Tutorial
·
Beginner
·
+10XP
·
40 mins
·
(181)
Unity Technologies

In this tutorial, you will learn how to optimize VR and AR experiences in Unity
Languages available:
1. Optimizing your VR/AR Experiences
Verified with: 2019.4 LTS
Optimization is a key ingredient to ensuring comfort and safety in XR applications. With Virtual reality, especially, it’s important that your application maintain a high frame rate. If it’s either CPU or GPU bound, the frame rate may drop and push a user to the brink of motion sickness.

One important metric is draw calls. To draw a GameObject on the screen, the engine has to issue a draw call to the graphics API. Draw calls are often resource-intensive, with the graphics API doing significant work for each one, causing performance overhead on the CPU side. This is mostly due to the state changes between the draw calls (such as switching to a different Material), which cause resource-intensive validation and translation steps in the graphics driver.
You’ll want to keep our draw calls and tri count as low as possible to reduce the computational load on the CPU and GPU.
There are a few straightforward techniques to implement when optimizing our applications. This tutorial will cover some of the standard techniques and strategies for optimizing Unity applications. Those techniques are:
1. Light baking
2. Occlusion culling
3. Static batching
4. Quality settings
5. Rendering pass type: single-pass (vs. multi-pass)
Light baking reduces the computational expense of rendering the Scene. This technique, which helps the GPU, refers to the process of pre-calculating Scene illumination before run time. This means there is no run-time overhead for baked lights. Normally, Unity renders each object for every light that shines on it. So, if an object has five lights on it, Unity will render the object five times (when in view). This can spike the tri count and number of draw calls, slowing application performance. Baking your lights is an effective way to reduce the number of tris and draw calls, since these calculations are done prior to run time (Figure 01).

Figure 01: Baking lights, as opposed to rendering each object a light hits, reduces the GPU workload.
Another technique used to reduce the workload of the GPU is Occlusion Culling. Occlusion Culling keeps Unity from rendering objects out of view of the Camera GameObject. By default, Unity does not render objects outside of the viewing frustum. However, it will still render objects blocked by objects in the foreground, which leads to overdraw, or pixel overlap (pixels drawn twice, over each other). This pixel overlap is unnecessary work and should be avoided. You can avoid this by using Occlusion Culling to “cull” any objects covered by other objects (Figure 02).


Figure 02: Occlusion Culling means objects that aren’t in view don’t get rendered.
Static batching improves both GPU and CPU performance. By making all stationary objects static, Unity combines stationary GameObjects into one big Mesh, rendering them faster. This reduces draw calls and physics calculations, as well. By tagging objects as static, Unity knows these objects will never move and therefore do not need to be included in physics calculations. If your application is CPU-bound, then marking more objects as static is a helpful technique.
Quality settings (Figure 03) control the graphical quality of objects being rendered (menu: Edit > Project Settings > Quality). These are out-of-the-box settings (pixel light count, light map resolution, etc.) that can either maximize graphical quality or maximize performance (Figure 04). The lower the graphical quality, the higher the performance.

Figure 03: Quality settings levels for various platforms

Figure 04: You can modify settings for specific quality levels.
And finally, you can adjust the rendering methods. Currently, Unity supports three rendering methods for XR:
- Multi-pass
- Single-pass
- Single-pass instanced
Single-pass instanced is the most performant of the three, but is not available on all devices.
Because Google VR has two Textures, one for each eye, Unity needs to render a Scene twice (Figure 05). With multi-pass rendering, Unity attempts to avoid duplicating work required for each eye, such as shadows, which don’t need to be rendered twice. Multi-pass still renders most objects twice, but renders the Scene graph once. This creates more accurate lighting, but comes at a computational cost, since the two renderings do not share GPU work across Textures. This is the least efficient rendering path, but works on most devices.


Figure 05: Multi-pass rendering renders most objects twice.
Single-pass, on the other hand, packs the two Textures into one big Texture (known as a double-wide Texture). It goes through the Scene graph only once, so it’s much faster on the CPU. However, it requires a lot of extra GPU state changes to accomplish this.
Single-pass instancing allows for even simpler integration and better performance. It reduces the CPU overhead, just like single-pass (lowering the number of draw calls), but also reduces the GPU overhead more than single-pass. The GPU is able to more efficiently process the draws, and you minimize state updates by not having to change the viewport between draws unlike with traditional single-pass.
You can read more about Single-pass instancing here


Figure 06: Single-pass instancing reduces GPU overhead.
2. Setting Up Light Baking
1. Set all non-moving objects to Static in your Scene. This is how to include them in lighting calculations.
2. Change each of your Scene’s light modes to Baked (Figure 07).

Figure 07: Changing light modes to Baked
3. In the Lighting settings window, click Generate Lighting (or ensure that B is ticked).
4. A progress bar will appear in the Editor’s status bar, in the bottom-right corner.
5. When baking is complete, you can see all the baked lightmaps in the Global Maps and Object Maps tabs of the Lighting window.
6. Try adjusting the lightmap size for increased lighting accuracy. This will increase your bake time and build file size, but it will also increase lighting precision.
3. Setting up Occlusion Culling:
1. Tag all objects occluding other objects as Occluder Static and all small, occluded objects as Occludee Static. The fastest way to do this is to multi-select the objects you want to be included in occlusion calculations and tag them appropriately.
2. If rendering a window, you should tag the window as Occludee Static, since it does not occlude any objects behind it.
3. Open the Occlusion window (Window > Rendering > Occlusion Culling).
4. Open the Occlusion Culling - Bake tab, and click Bake. This will bake your occlusion map so your Occlusion Culling works properly (Figure 08).

Figure 08: Baking your occlusion map
4. Setting Up Static Batching
1. Navigate to Player Settings (Edit > Project Settings > Player).
2. Enable Static Batching (Figure 09).

Figure 09: Enabling Static Batching
3. In the Hierarchy, select all non-moving objects (static objects) and tag them as Batching Static. Note: the common practice is to toggle the checkbox, which toggles on all Static tags (Figure 10).

Figure 10: Selecting Batching Static allows for faster rendering.
5. Setting Up Quality Settings
1. Navigate to Quality settings (Edit > Project Settings > Quality)
2. Select Fastest, to maximize performance (Figure 11).
3. Take a moment to explore the adjusted quality settings below the presets.

Figure 11: Selecting Fastest maximizes performance.
6. Setting Up Each Rendering Pass Type
1. To enable single-pass or single-pass instanced, navigate to Project Settings.
2. Select the Player category.
3. Navigate to the XR Settings panel at the bottom.
4. Check the Virtual Reality Supported option.
5. Select Single Pass Instanced from the Stereo Rendering Mode drop-down menu (Figure 12).

Figure 12: Setting up Single Passed Instanced
7. Conclusion
These five techniques can help reduce performance hiccups (and the possibility of making your users queasy) in your VR/AR projects without requiring you to modify existing Assets. While these techniques are crucial for VR/AR development, they are good to employ in mobile, console, and desktop applications as well.