When developing VR (virtual reality) games, reducing the rendering load and raising the frame rate to the maximum is an absolute requirement to prevent VR sickness and provide a comfortable experience. As a speed-up approach, Unity often uses the "Single Pass Instanced" (or Single Pass Stereo) feature, which processes both left and right images in one draw call. However, once this setting is enabled, when applying post-process effects such as Bloom, Blur, and Depth of Field (DoF), a fatal drawing bug may occur, such as ``Although the left eye screen is drawn normally, the position of the post effect on the right eye screen shifts significantly to the right, or the right eye effect is stretched and collapsed.''

In this article, we will identify the cause of the post effect being shifted only for the right eye when Single Pass Stereo is enabled, and explain the correct sampling macro and implementation procedure to make custom shaders and post effects compatible with VR.

For beginners: How would you describe this problem in real life?

Let's compare the drawing misalignment problem of the right eye in VR to real-life "3D movie glasses and screen."

VR goggles express a three-dimensional space by projecting two different images slightly shifted to the side by the same distance as the distance between the human eyes (pupillary distance) onto the lenses for our left and right eyes.

Single Pass Instanced is an ultra-high-speed drawing method that draws a picture for the left eye and a picture for the right eye at the same time on one huge piece of drawing paper (texture array).

However, if the shader (image processing filter) that applies the post effect does not understand the structure of the special drawing paper for VR, it can cause mistakes such as ``Even when processing the picture of the right eye, it mistakenly applies the filter based on the coordinates (position information) of the picture of the left eye.'' As a result, when watching a movie while wearing 3D glasses, only the image on the right eye side is pulled toward the left eye's position and overlaps with the image, causing the eyes to become confused and causing severe dizziness and headaches. To resolve this, it is necessary to correctly declare to the shader, "This is a special drawing paper for VR with separate left and right eyes," and to switch the sampling method (macro) so that when processing the right eye, it automatically reads pixels from the right eye position.

Problem symptoms and specific phenomena

When this VR drawing misalignment bug occurs, the following symptoms are observed.

  • When viewed while wearing a VR headset, the left eye is perfectly in focus, but the position of the light overflow (Bloom) and blur of the image of the right eye (Blur) is misaligned with the outline of the 3D model, causing an extremely unpleasant stereoscopic discomfort (eye pain).
  • The bug cannot be reproduced in the normal game view (single camera display) of the Unity editor, and it only occurs noticeably when VR is split into left and right displays or when building an actual device, so debugging and detection are likely to be delayed.
  • This often occurs when introducing a custom post effect shader that you have created or an old post effect purchased from the asset store.

Possible cause: Why is only the right eye misaligned?

When using Single Pass Instanced, Unity combines the left and right eye rendering targets into a single Texture2DArray and processes each eye image as a slice (index 0 for the left eye, index 1 for the right eye). However, older shaders for typical 2D games and non-VR applications declare the screen buffer as a regular 2D texture (sampler2D) and sample it with simple uv coordinates.

If you run this shader as is in a VR environment, the shader will try to obtain pixels from the "normal 2D texture (slice 0)" even while the right eye is being drawn (slice 1), so the sampling coordinates of the right eye image will be incorrect, and the left eye information will be superimposed on the right eye, causing the position to shift. To prevent this, you need to use a macro that automatically recognizes which eye is the current one (unity_StereoEyeIndex) when sampling.

Specific solutions and approaches

To solve this problem, rewrite the texture declaration and sampling process in the post-effect shader to stereo screen space compatible macros (HLSL / CG) provided by Unity as standard.

1. Rewriting screen texture declaration

Open the shader file (.shader) and modify the declaration part of the input main texture (screen buffer) from the usual Texture2D to a declaration macro compatible with both VR.

// × Wrong old declaration
// sampler2D _MainTex;

// ◯ Correct VR compatibility declaration
UNITY_DECLARE_SCREENSPACE_TEXTURE(_MainTex);

2. Rewriting sampling process

Change the part where texture color information is sampled in the pixel shader (fragment shader) to a macro that automatically takes into account the current eye index and samples it.

float4 frag(v2f i) : SV_Target
{
    // × Wrong old sampling
    // float4 col = tex2D(_MainTex, i.uv);

    // ◯ Correct VR compatible sampling (automatically selects UV and array slices according to eye index)
    float4 col = UNITY_SAMPLE_SCREENSPACE_TEXTURE(_MainTex, i.uv);
    
    return col;
}

It is also essential to embed the UNITY_VERTEX_INPUT_INSTANCE_ID and UNITY_VERTEX_OUTPUT_STEREO macros in the input/output structure of the vertex shader and call UNITY_SETUP_INSTANCE_ID.

Confirmation checklist useful in practice

This is a checklist to check the safety of the post process before building VR.

Is Stereo Rendering Mode in XR Plug-in Management in
Confirmation items Standards/measures
Macro coverage Are UNITY_SAMPLE_SCREENSPACE_TEXTURE used in all custom shaders related to post effects?
Check XR SettingsProject Settings set to Single Pass Instanced?
Actual device test (binocular) When wearing the actual headset and quickly shaking the head from side to side, check to see if the post-effect applied part moves with a delay or only the right eye is shaken.

Summary

Reducing the load using Single Pass Instanced is important in VR development, but lack of stereo support in post-effect shaders causes serious 3D motion sickness and eye fatigue for players. By simply replacing the screen texture declaration and sampling method with Unity's official stereo-compatible macros (such as UNITY_DECLARE_SCREENSPACE_TEXTURE), this right eye misalignment problem will be completely resolved and you will be able to provide a beautiful and comfortable VR experience.