Version: 2020.1
言語: 日本語
シングルパスステレオレンダリング (2 倍幅のレンダリング)
HoloLens のシングルパスステレオレンダリング

シングルパスインスタンシングレンダリング

シングルパスインスタンシング レンダリング (ステレオインスタンシングとも呼ばれます) を使用すると、GPU は各ドローコールをインスタンス化したドローコールに置き換えて、1 つのレンダリングパスを実行します。これを使用すると、2 つのドローコール間のキャッシュコヒーレンシのため、CPU 使用率を著しく低下させ、GPU 使用率もわずかに低下させることができます。これにより、アプリケーションの消費電力をはるかに削減できます。

サポートするプラットフォーム

  • PlayStation VR

  • Oculus Rift (DirectX 11)

  • HoloLens

  • Magic Leap

  • Multiview 拡張をサポートする Android デバイス

  • デスクトップの DirectX の場合は、GPU は Direct3D 11 と VPAndRTArrayIndexFromAnyShaderFeedingRasterizer 拡張をサポートする必要があります。

  • デスクトップの OpenGL の場合は、GPU は以下の拡張のうち 1 つをサポートする必要があります。
    • GL_NV_viewport_array2
    • GL_AMD_vertex_shader_layer
    • GL_ARB_shader_viewport_layer_array

ノート: Unity は、ディファードレンダリングを使用している場合、古いレンダーパイプラインでシングルパスステレオインスタンシングをサポートしません。

この機能を有効にするには Player 設定を開きます (Edit > Project Settings > Player)。Player 設定の XR Settings に移動し、Virtual Reality Supported チェックボックスをチェックし、それから Stereo Rendering Method ドロップダウンから Single Pass Instanced (Preview) を選択します。

XR Settings で Stereo Rendering Method を Single Pass Instanced (Preview) に設定
XR SettingsStereo Rendering MethodSingle Pass Instanced (Preview) に設定

デフォルトの Stereo Rendering MethodMulti Pass です。この設定は高速ではありませんが、カスタムのシェーダーに適しています。カスタムシェーダーの場合は、Single Pass Instanced レンダリングに対応できるようにシェーダーの調整が必要な場合があります。

カスタムシェーダー

以下の手順を実行する前に、カスタムシェーダーでインスタンシングを利用できるように更新してください (GPU インスタンシング を参照)。

次に、カスタムシェーダーのフラグメントシェーダー (Vertex/Hull/Domain/Geometry) より前の最後のシェーダーステージに、さらに 2 つの変更を加える必要があります。

シングルパスインスタンシングに対応させたい各カスタムシェーダに、以下の手順を実行します。

ステップ 1: appdata 構造体に UNITY_VERTEX_INPUT_INSTANCE_ID を加えます。

struct appdata
{
    float4 vertex : POSITION;
    float2 uv : TEXCOORD0;

    UNITY_VERTEX_INPUT_INSTANCE_ID //挿入
};

ステップ 2: v2f 出力構造体に UNITY_VERTEX_OUTPUT_STEREO を加えます。

struct v2f
{
    float2 uv : TEXCOORD0;
    float4 vertex : SV_POSITION;

    UNITY_VERTEX_OUTPUT_STEREO //挿入

};

ステップ 3: Main の vert メソッドの最初に UNITY_SETUP_INSTANCE_ID() マクロを加え、その後に UNITY_INITIALIZE_OUTPUT(v2f, o)UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO() の呼び出しを加えます。

UNITY_SETUP_INSTANCE_ID() は、現在どちらの目を GPU がレンダリングしているかに基づいて、unity_StereoEyeIndexunity_InstanceID の Unity のビルトインシェーダー変数を正しい値に計算し、設定します。

UNITY_INITIALIZE_VERTEX_OUTPUT_STEREOunity_StereoEyeIndex の値に基づいてレンダリング先になるテクスチャ配列の目を GPU に伝えます。このマクロは、unity_StereoEyeIndex の値を頂点シェーダーから転送し、フラグメントシェーダーの frag メソッドで UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEXが呼び出された場合にのみ、フラグメントシェーダーでアクセスできるようにします。

UNITY_INITALIZE_OUTPUT(v2f,o) はすべての v2f 値を 0 に初期化します。

v2f vert (appdata v)
{
    v2f o;

    UNITY_SETUP_INSTANCE_ID(v); //挿入
    UNITY_INITIALIZE_OUTPUT(v2f, o); //挿入
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); //挿入

    o.vertex = UnityObjectToClipPos(v.vertex);

    o.uv = v.uv;

    return o;
}

ポストプロセスシェーダー

ポストプロセスシェーダーでシングルパスインスタンシングをサポートする場合は、カスタムシェーダーの手順と以下の手順に従ってください。すべての Unity の基本的なシェーダーソースは、Unity ダウンロード アーカイブ からダウンロードできます。

シングルパスインスタンシングに対応させたい各ポストプロセスシェーダーに、以下の手順を実行します。

ステップ 1: シェーダーメソッドの frag メソッドの外側に UNITY_DECLARE_SCREENSPACE_TEXTURE(tex) マクロを加えます (配置例は下の例を参照)。 そうすると、特定のステレオレンダリング方法を使用する場合、GPU は適切なテクスチャサンプラーを使用します。例えば、マルチパスレンダリングを使用する場合、GPU は Texture2D サンプラーを使用します。シングルパスインスタンシングまたはマルチビューレンダリングの場合、テクスチャサンプラーはテクスチャ配列です。

ステップ 2: フラグメントシェーダー frag メソッドの前に UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i) を加えます (配置例は下の例を参照)。unity_StereoEyeIndex ビルトインシェーダー変数を使用して、GPU がどの目にレンダリングするかを調べる場合にのみ、このマクロを追加する必要があります。これは、ポストプロセスエフェクトをテストするときに便利です。

ステップ 3: 2D テクスチャをサンプリングするときに UNITY_SAMPLE_SCREENSPACE_TEXTURE() を使用します (配置例は下の例を参照)。スタンダードシェーダーは 2D テクスチャベースのバックバッファを使用してテクスチャをサンプリングします。シングルパスステレオインスタンシングはこのタイプのバックバッファを使用しないため、2D テクスチャのサンプリングに別の方法を指定しないと、シェーダーは正しくレンダリングしません。レンダリングの問題を防ぐために、UNITY_SAMPLE_SCREENSPACE_TEXTURE() マクロはどのステレオレンダリングパスを使用しているかを検出し、自動的に適切な方法でテクスチャをサンプリングします。 深度テクスチャとスクリーンスペースシャドウマップに使用される類似のマクロの詳細については、HLSLSupport.cginc を参照してください。

UNITY_DECLARE_SCREENSPACE_TEXTURE(_MainTex); //挿入

fixed4 frag (v2f i) : SV_Target
{
    UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i); //挿入
    
    fixed4 col = UNITY_SAMPLE_SCREENSPACE_TEXTURE(_MainTex, i.uv); //挿入
    
    // 色を反転させます    
    col = 1 - col;
    
    return col;
}

完全なサンプルシェーダーコード

以下はイメージエフェクトシェーダーの簡単なテンプレートの例です。前述の変更がすべて反映され、シングルパスインスタンシングに適応可能になっています。シェーダーコードで追加された部分にはコメント (//挿入) が入っています。

struct appdata
{
    float4 vertex : POSITION;
    float2 uv : TEXCOORD0;
    
    UNITY_VERTEX_INPUT_INSTANCE_ID //挿入
};

//v2f 出力構造体

struct v2f
{

    float2 uv : TEXCOORD0;
    float4 vertex : SV_POSITION;
    
    UNITY_VERTEX_OUTPUT_STEREO //挿入
};

v2f vert (appdata v)
{
    v2f o;
    
    UNITY_SETUP_INSTANCE_ID(v); //挿入
    UNITY_INITIALIZE_OUTPUT(v2f, o); //挿入
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); //挿入
    
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = v.uv;
    return o;
}

UNITY_DECLARE_SCREENSPACE_TEXTURE(_MainTex); //挿入

fixed4 frag (v2f i) : SV_Target
{
    UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i); //挿入
    
    fixed4 col = UNITY_SAMPLE_SCREENSPACE_TEXTURE(_MainTex, i.uv); //挿入
    
    // 色を反転させます    
    col = 1 - col;
    
    return col;
}

プロシージャルジオメトリ

Graphics.DrawProceduralIndirect() および CommandBuffer.DrawProceduralIndirect() メソッドを使用して GPU に完全にプロシージャルなジオメトリを描画する場合、両方のメソッドが コンピュートバッファから引数を受け取ることに注意してください。つまり、ランタイムにインスタンス数を増やすことは難かしいということです。インスタンス数を増やすには、コンピュートバッファに含まれるインスタンス数を手動で 2 倍にする必要があります。

シェーダーコードに関する詳細は頂点シェーダーとフラグメントシェーダーの例 のページを参照してください。

シェーダーのデバッグ

次のシェーダーコードは、ゲームオブジェクトをユーザーの左目用に緑色に、右目用に赤色にレンダリングします。このシェーダーは、すべてのステレオグラフィックスが機能し、正しく機能していることを確認できるため、ステレオレンダリングのデバッグに便利です。

Shader "XR/StereoEyeIndexColor"
{
   Properties
   {
       _LeftEyeColor("Left Eye Color", COLOR) = (0,1,0,1)
       _RightEyeColor("Right Eye Color", COLOR) = (1,0,0,1)
   }

   SubShader
   {
      Tags { "RenderType" = "Opaque" }

      Pass
      {
         CGPROGRAM

         #pragma vertex vert
         #pragma fragment frag

         float4 _LeftEyeColor;
         float4 _RightEyeColor;

         #include "UnityCG.cginc"

         struct appdata
         {
            float4 vertex : POSITION;

            UNITY_VERTEX_INPUT_INSTANCE_ID
         };

         struct v2f
         {
            float4 vertex : SV_POSITION;

            UNITY_VERTEX_INPUT_INSTANCE_ID 
            UNITY_VERTEX_OUTPUT_STEREO
         };

         v2f vert (appdata v)
         {
            v2f o;

            UNITY_SETUP_INSTANCE_ID(v);
            UNITY_INITIALIZE_OUTPUT(v2f, o);
            UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);

            o.vertex = UnityObjectToClipPos(v.vertex);

            return o;
         }

         fixed4 frag (v2f i) : SV_Target
         {
            UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);

            return lerp(_LeftEyeColor, _RightEyeColor, unity_StereoEyeIndex);
         }
         ENDCG
      }
   }
}
シングルパスステレオレンダリング (2 倍幅のレンダリング)
HoloLens のシングルパスステレオレンダリング