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;
}
以上。誰かの役にたてば幸いです。