Rendering in VR
Rendering is one of the most recurring bottlenecks in VR projects. Optimizing rendering is essential to building a comfortable and enjoyable experience in VR.
Stereo Rendering Modes
Setting Stereo Rendering Method to Single Pass Instanced or Single Pass in the XR Settings section of Player Settings will allow for performance gains on the CPU and GPU. See the Single Pass Instanced Rendering manual page and the Single Pass Stereo Rendering manual page for more details. Additionally, refer to this blog post to read more about stereo rendering modes.
Batching is a good way to reduce work on the GPU by minimizing the cost of context switching for each draw call (i.e., uploading a new shader, its textures, and other data). Batching relies on having shared Materials and does have some implications on memory and the CPU. Please refer to the Draw Call Batching manual page for more information about Static and Dynamic Batching.
Use GPU Instancing to draw (or render) multiple copies of the same mesh at once, using a smaller number of draw calls. It is useful for drawing objects such as buildings, trees and grass, or other things that appear repeatedly in a Scene. See the GPU Instancing manual page to learn how to enable GPU Instancing in your project.
Every lighting strategy has its pros, cons, and implications. Using fully realtime lighting and realtime global illumination in your VR project is not suggested as it has important impacts on rendering performance. For most projects, we suggest that you favor the use of non-directional lightmaps for static objects and the use of light probes for dynamic objects instead of rely on realtime lighting.
The Lighting Strategy manual page clearly explains the different lighting strategies offered by Unity and identifies two strategies as being the most ideal for VR. The first suggestion is to bake all lights combined with light probes and the second is to use the mixed lighting with Shadowmask combined with light probes. The most notable difference between these two strategies is that the latter allows real-time specular and shadows for Dynamic objects.
There are several best practices to keep in mind when working with cameras in VR.
The camera’s orientation and position (for platforms supporting 6 degrees of freedom) should always respond to the user’s motion, no matter which of camera viewpoint is used.
Actions that affect camera movement without user interaction can lead to simulation sickness. Avoid using camera effects similar to "Walking Bob" commonly found in first-person shooter games, camera zoom effects, camera shake events, and cinematic cameras.
Unity obtains the stereo projection matrices from the VR SDKs directly. Overriding the field of view manually is not allowed.
Depth of field or motion blur post-process effects affect a user's sight and often lead to simulation sickness.
Moving or rotating the horizon line or other large components of the environment can affect the user’s sense of stability and should be avoided.
Set the near clip plane of the first-person camera(s) to the minimal acceptable value for correct rendering of objects. Set your far clip plane to a value that optimizes frustum culling.
When using Canvas, favor World Space render mode over Screen Space render modes, as it very difficult for a user to focus on Screen Space UI.
In VR, most image effects are expensive as they are rendering the scene twice - once for each eye. As many post-processes require full screen draws, reducing the number of post-processing passes helps overall rendering performance. Full-frame post process effects are very expensive and should be used cautiously as they incur important implications on GPU fill rate.
As mentioned previously, the use of post-process effects such as depth of field or motion blur are not suggested in VR as they may to simulation sickness.
If you need to use post-processing effects in your project, we suggest that you use the Post-Processing Stack as it can merge several enabled effects into fewer passes. The Post-Processing Stack can also be extended to include your custom effects.
Anti-aliasing is highly desirable in VR as it helps to smooth the image, reduce jagged edges, and minimize specular aliasing.
When using Deferred Rendering, consider using an anti-aliasing post-processing effect. Unity’s Post-Processing Stack (mentioned above) offers the following:
FXAA - This is the cheapest solution for anti-aliasing and is recommended for low-end PC.
TAA - This state-of-the-art technique will give better results than the other techniques at a higher GPU cost. We recommend the use of this technique on high-end PCs.
Optimizing the shaders used in your project is an important step in improving your rendering performance. Always review shaders generated by visual shader editors, including those created from Shader Graph, as they may be unoptimized. See the Shader Performance guide for tips concerning shader optimization.
Graphics drivers do not actually prepare shaders until they are first needed. We suggest that you call use a Shader Variant Collection and call its WarmUp function at an opportune time (e.g., while displaying a loading screen for example) in order prepare essential shaders and to ultimately avoid frame rate hiccups when using a shader for the first time.
Some shaders, especially post-processing effects, may require some modifications in order to function correctly when using Single-Pass Stereo Rendering. Please refer to the Authoring and modifying Shaders to support Single-Pass Stereo rendering section of the Single-Pass Stereo Rendering manual page for more details.
Setting XRSettings.eyeTextureResolutionScale to a value below 1.0 will lead to decreased usage of the GPU at the cost of image quality whereas setting this property to a value above 1.0 will lead to a crisper image at the cost of GPU timings and a larger memory footprint. It’s important to note that setting XRSettings.eyeTextureResolutionScale will reallocate Renderer Textures which will ultimately cause a hitch due to expensive memory allocation operations being called. Consider changing this property when starting the app, from an options screen with quality settings, or when entering scenes that require more or less GPU bandwidth.
Setting XRSettings.renderViewportScale, on the other hand, allows you to specify how much of the allocated eye textures to use. This property can be set without any frame rate hitches given that no textures are reallocated. This also implies that this setting accepts values between 0 and 1.0; 1.0 being the default value. Use this property to gain performance on the GPU in more demanding scenes of your project.
Most VR platforms have implemented a graphics technique called asynchronous reprojection which helps VR applications hit target frame rates during more computation heavy frames. Whenever an application drops a frame, asynchronous reprojection will kick in, rendering a new frame by applying the user’s latest orientation to the most recent rendered frame. Oculus, GearVR, Daydream, SteamVR, and PlayStation VR all have their own proprietary implementation of this technique. Although asynchronous reprojection is enabled by default on all applications that support it, it is important for the developer to understand this feature when comes the time to debug and optimize. Developers should never rely on this technique in place of optimization as this technique does cause positional and animation judder.