LoginSignup
1
1

More than 1 year has passed since last update.

UnityでgeometryシェーダのSingle Pass Instanced対応方法

Posted at

Unityの自作シェーダをHoloLens2などのXR環境で動かすと、右目だけ描画されないことがある。
一番簡単な対応方法はレンダリング方式を「Multi Pass」にすること(参考ページ)だが、
パフォーマンスなどの理由で「Single Pass Instanced」方式にシェーダを対応させたい場合に必要な作業についてまとめる。

公式ドキュメントにはvertexシェーダとfragmentシェーダだけの例は載ってるが、geometryシェーダを含むものを作ろうとして難儀したため。

対応方法の原則

各シェーダ関数間でUNITY_VERTEX_INPUT_INSTANCE_IDおよびUNITY_VERTEX_OUTPUT_STEREOの値がうまく受け渡されればよい。
(試してはいないが、おそらくテッセレーションシェーダを書く場合でもこのルールを守ればうまくいくと思われる)
以下、シェーダ関数への入力値をi、出力値をoとする。

構造体への対応

  • シェーダ間で受け渡すすべての構造体にUNITY_VERTEX_INPUT_INSTANCE_IDを追加する。
  • appdata以外のシェーダ間で受け渡す構造体にUNITY_VERTEX_OUTPUT_STEREOを追加する。

関数への対応

  • vertexシェーダでは以下を実行
    • 最初にUNITY_SETUP_INSTANCE_ID(i)を実行
    • どこかでUNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o)を実行
  • vertexシェーダ以外では、iを使う前にUNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i)を実行
  • fragmentシェーダ以外で以下を実行
    • UNITY_TRANSFER_INSTANCE_ID(i, o)
    • UNITY_TRANSFER_VERTEX_OUTPUT_STEREO(i, o)

単純なジオメトリシェーダの例

以下はジオメトリシェーダでtriangleをいじりたいような場合の例:

#pragma vertex vert
#pragma geometry geom
#pragma fragment frag

// vertexへの入力構造体
struct appdata
{
    float4 vertex : POSITION;
    float2 uv : TEXCOORD0;
    UNITY_VERTEX_INPUT_INSTANCE_ID
};

//vertexの出力でありgeometryの入力となる構造体
struct v2g
{
    float2 uv : TEXCOORD0;
    UNITY_VERTEX_INPUT_INSTANCE_ID
    UNITY_VERTEX_OUTPUT_STEREO
};

//geometryの出力でありfragmentの入力となる構造体
struct g2f
{
    float2 uv : TEXCOORD0;
    float4 pos : SV_POSITION;
    UNITY_VERTEX_INPUT_INSTANCE_ID
    UNITY_VERTEX_OUTPUT_STEREO
};

v2g vert (appdata v)
{
    v2f o;
    
    UNITY_SETUP_INSTANCE_ID(v);
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
    
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = v.uv;
    return o;
}

[maxvertexcount(3)]
void geom(triangle v2g IN[3], inout TriangleStream<g2f> triStream)
{
    g2f o;
 
    // サンプルなので入力をそのまま出力
    for(int j = 0; j < 3; j++)
    {
        v2g i = IN[j];
        // 個々のiとoに対してSingle Pass Instanced対応を実施
        UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i)
        o.pos = i.pos;
        UNITY_TRANSFER_INSTANCE_ID(i, o);
        UNITY_TRANSFER_VERTEX_OUTPUT_STEREO(i, o);
        triStream.Append(o);
    }
}

UNITY_DECLARE_SCREENSPACE_TEXTURE(_MainTex);

fixed4 frag (g2f i) : SV_Target
{
    UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
    
    fixed4 col = UNITY_SAMPLE_SCREENSPACE_TEXTURE(_MainTex, i.uv);
    
    return col;
}

以上。誰かの役にたてば幸いです。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1