VR(バーチャルリアリティ)ゲーム開発において、描画負荷を軽減しフレームレートを極限まで引き上げることは、VR酔いを防ぎ快適な体験を提供する上で絶対条件です。この高速化アプローチとして、Unityでは左右両目の画像を1回のドローコールでまとめて処理する「Single Pass Instanced (または Single Pass Stereo)」機能がよく採用されます。しかし、この設定を有効にした途端、ブルーム(Bloom)やブラー(Blur)、被写界深度(DoF)などのポストプロセスエフェクトを適用した際、「左目の画面は正常に描画されているのに、右目の画面だけポストエフェクトの位置が大きく右にズレる、あるいは右目だけエフェクトが引き伸ばされて崩れてしまう」という致命的な描画バグが発生することがあります。

本記事では、Single Pass Stereo有効時にポストエフェクトが右目だけズレる原因を特定し、カスタムシェーダーやポストエフェクトをVR対応にするための正しいサンプリングマクロと実装手順を解説します。

初心者向け:この問題を現実で例えると?

このVRにおける右目の描画ズレ問題を、現実の「3D映画のメガネとスクリーン」に例えてみましょう。

VRゴーグルは、私たちの左目用と右目用のレンズに対して、人間の両目の間隔(瞳孔間距離)と同じだけ少し横にズラした2枚の異なる絵を映し出すことで、立体的な空間を表現しています。

Single Pass Instancedという描画方法は、「左目用の絵と右目用の絵を、1枚の超巨大な画用紙(テクスチャ配列)に同時に描き上げる」という超高速な描画手法です。

しかし、ポストエフェクトをかけるシェーダー(画像処理フィルター)がVR用の特殊な画用紙の構造を理解していないと、「右目の絵を加工する際にも、間違えて左目の絵の座標(位置情報)を基準にしてフィルターを塗ってしまう」というミスを起こします。結果として、3Dメガネをかけて映画を見ている際、右目側の映像だけが左目の位置に引っ張られて大きくズレて重なり、目がちぐはぐになって激しい目眩や頭痛を引き起こすことになります。これを解決するには、シェーダーに対して「これは左右の目が分かれたVR用の特殊な画用紙です」と正しく宣言し、右目を処理する時は右目用の位置から自動でピクセルを読み取るように、サンプリングのやり方(マクロ)を切り替える必要があります。

不具合の症状と具体的な現象

このVR描画ズレバグが発生した際、以下のような症状が見られます。

  • VRヘッドセットを装着して見ると、左目は完璧にフォーカスが合っているが、右目の映像の光の溢れ(Bloom)やボケ足(Blur)の位置が3Dモデルの輪郭とズレており、極めて不快な立体違和感(目の痛み)が生じる。
  • Unityエディタの通常のゲームビュー(単一カメラ表示)ではバグが再現されず、VRの左右二分割表示や実機ビルド時にのみ顕著に発生するため、デバッグや検知が遅れやすい。
  • 自作したカスタムポストエフェクト用シェーダーや、アセットストアで購入した古いポストエフェクトを導入した際に頻発する。

想定される原因:なぜ右目だけズレるのか?

Single Pass Instancedを使用する場合、Unityは左右の目のレンダリングターゲットを1つの「テクスチャ配列 (Texture2DArray)」にまとめ、それぞれの目の画像をスライス(左目がインデックス0、右目がインデックス1)として処理します。 しかし、一般的な2Dゲームや非VR用の古いシェーダーでは、画面のバッファを通常の2Dテクスチャ(sampler2D)として宣言し、単純な uv 座標でサンプリングを行っています。

このシェーダーをそのままVR環境で走らせると、右目の描画(スライス1)が行われている最中であっても、シェーダーは「通常の2Dテクスチャ(スライス0)」からピクセルを取得しようとするため、右目の映像のサンプリング座標が狂い、左目の情報が右目に重ね合わされて位置がズレてしまうのです。これを防ぐためには、サンプリング時に現在の目がどちらであるか(unity_StereoEyeIndex)を自動認識するマクロを使用する必要があります。

具体的な解決方法とアプローチ

この問題を解決するには、ポストエフェクト用シェーダー内のテクスチャ宣言とサンプリング処理を、Unityが標準で提供しているステレオスクリーンスペース対応マクロ(HLSL / CG)に書き換えます。

1. 画面テクスチャ宣言の書き換え

シェーダーファイル(.shader)を開き、入力されるメインテクスチャ(画面バッファ)の宣言部分を、通常の Texture2D からVR両対応の宣言マクロに修正します。

// × 誤った古い宣言
// sampler2D _MainTex;

// ◯ 正しいVR対応宣言
UNITY_DECLARE_SCREENSPACE_TEXTURE(_MainTex);

2. サンプリング処理の書き換え

ピクセルシェーダー(フラグメントシェーダー)内でテクスチャの色情報をサンプリングしている箇所を、現在の目のインデックスを自動で考慮してサンプリングするマクロに変更します。

float4 frag(v2f i) : SV_Target
{
    // × 誤った古いサンプリング
    // float4 col = tex2D(_MainTex, i.uv);

    // ◯ 正しいVR対応サンプリング (目のインデックスに応じてUVと配列スライスを自動選択する)
    float4 col = UNITY_SAMPLE_SCREENSPACE_TEXTURE(_MainTex, i.uv);
    
    return col;
}

また、頂点シェーダーの入力・出力構造体に UNITY_VERTEX_INPUT_INSTANCE_IDUNITY_VERTEX_OUTPUT_STEREO マクロを埋め込み、UNITY_SETUP_INSTANCE_ID を呼び出すことも必須となります。

実務で役立つ確認チェックリスト

VRビルド前にポストプロセスの安全性を確認するためのチェックリストです。

確認項目 基準・対策
マクロの網羅 ポストエフェクト関連のカスタムシェーダー全てで UNITY_SAMPLE_SCREENSPACE_TEXTURE が使用されているか。
XR Settingsの確認 Project Settingsの XR Plug-in Management 内の Stereo Rendering ModeSingle Pass Instanced になっているか。
実機テスト(両眼) 実機ヘッドセットを被り、頭を素早く左右に振った際、ポストエフェクト適用部分が遅れて動いたり右目だけブレたりしないか確認する。

まとめ

VR開発において Single Pass Instanced による負荷削減は重要ですが、ポストエフェクト用シェーダーのステレオ対応漏れはプレイヤーに深刻な3D酔いと目の疲労を与えます。画面テクスチャの宣言やサンプリング方法をUnity公式のステレオ対応マクロ(UNITY_DECLARE_SCREENSPACE_TEXTURE 等)へ置き換えるだけで、この右目ズレ問題は完璧に解消し、美しく快適なVR体験を提供できるようになります。