はじめに
本記事はAdvent Calendar 2022の7日目の記事です。
環境
- Unity 2021.3.11f1
- URP
- RenderDoc
Samplerには制限がある
問題の前に前提の話をします。
Samplerは最大で16個までしか使えません。
float4 frag (v2f i) : SV_Target
{
float4 col = tex2D(_MainTex0, i.uv);
col += tex2D(_MainTex1, i.uv);
col += tex2D(_MainTex2, i.uv);
col += tex2D(_MainTex3, i.uv);
// _MainTex4~14まである
col += tex2D(_MainTex15, i.uv);
// 17枚目のテクスチャはまだ使わない
// col += tex2D(_MainTex16, i.uv);
return col;
}
ここで17枚目のテクスチャを使ってみます。
float4 frag (v2f i) : SV_Target
{
float4 col = tex2D(_MainTex0, i.uv);
col += tex2D(_MainTex1, i.uv);
col += tex2D(_MainTex2, i.uv);
col += tex2D(_MainTex3, i.uv);
// _MainTex4~14まである
col += tex2D(_MainTex15, i.uv);
// 17枚目を使う
col += tex2D(_MainTex16, i.uv);
return col;
}
Shader error in 'Unlit/Test': maximum ps_4_0 sampler register index (16) exceeded at
エラー文ではSamplerが16を超えた的な内容が出ます。
何が問題なのか
Samplerの数=テクスチャの枚数
といった感じでShaderを書いていると16枚以上テクスチャを使えないことになります。
解決方法
ですがSamplerは使い回して使うことができるので1つのSamplerで何枚ものテクスチャを出すことができます。
これを利用すればSamplerは16個、Textureは128個まで使うことができます。
Texture2D _MainTex0;
SamplerState sampler_MainTex0;
Texture2D _MainTex1;
Texture2D _MainTex2;
// _MainTex3~_MainTex15まである
Texture2D _MainTex16;
float4 frag (v2f i) : SV_Target
{
float4 col = _MainTex0.Sample(sampler_MainTex0, i.uv);
col += _MainTex1.Sample(sampler_MainTex0, i.uv);
col += _MainTex2.Sample(sampler_MainTex0, i.uv);
// _MainTex3~_MainTex15まで上と同じような処理
col += _MainTex16.Sample(sampler_MainTex0, i.uv);
// これでTextureを0~16で17枚使用してる
return col;
}
しかし…
SamplerState
が消える?ことがあります。
Texture2D _MainTex0;
SamplerState sampler_MainTex0;
Texture2D _MainTex1;
float4 frag (v2f i) : SV_Target
{
float4 col=(float4)0;
float4 col2=(float4)0;
col = _MainTex0.Sample(sampler_MainTex0, i.uv);
col2=_MainTex1.Sample(sampler_MainTex0, i.uv);
// colは使わずcol2だけを返す
return col2;
}
最終的に返す値はcol2
なのでcol
の分の計算がすっ飛ばされて結果的にsampler_MainTex0
が消えてエラーを出します。
col
を返すときSamplerState
はしっかりと残っています。
return
までの計算でそのSamplerState
を使わないことが問題なので
float4 frag (v2f i) : SV_Target
{
float4 col=(float4)0;
float4 col2=(float4)0;
col = _MainTex0.Sample(sampler_MainTex0, i.uv);
col2=_MainTex1.Sample(sampler_MainTex0, i.uv);
// col,col2どっちも計算に使う
col2 = col2+col;
return col2;
}
このようにreturn
まででちゃんと数値を使ってあげればエラーはなくなります。
しかし、このままだと#if
して分岐するプログラムなどを書いたときにSamplerState
が消えてエラーが出る可能性があります。
解決策
エラー文を確認すると
Shader error in 'Unlit/Test': Fragment program 'frag': Unrecognized sampler 'sampler_maintex0' - does not match any texture and is not a recognized inline name (should contain filter and wrap modes)
'Unlit/Test' のシェーダー エラー: フラグメント プログラム 'frag': 認識されないサンプラー 'sampler_maintex0' - どのテクスチャとも一致せず、認識されたインライン名ではありません (フィルター モードとラップ モードを含める必要があります)
フィルターモードとラップモードを含める必要があるといわれるので素直に追加します。
Texture2D _MainTex0;
// これをやめて
// SamplerState sampler_MainTex0;
// こんな感じにする
SamplerState sampler_linear_clamp_MainTex0;
Texture2D _MainTex1;
float4 frag (v2f i) : SV_Target
{
float4 col=(float4)0;
float4 col2=(float4)0;
col = _MainTex0.Sample(sampler_linear_clamp_MainTex0, i.uv);
col2=_MainTex1.Sample(sampler_linear_clamp_MainTex0, i.uv);
// colは使わずcol2だけを返す
return col2;
}
フィルター
- Point
- Linear
- Trilinear
ラップ
- Clamp
- Repeat
- Mirror
- MirrorOnce
をSamplerState
の変数名に書き足せば使えます。
これらの名前が入っていればいいので
- sampler_linear_clamp_MainTex0(上で使ってた名前)
- samplerlinearclamp_MainTex0(_を一部消した)
- samplerclamp_MainTex0_linear(linearを最後に書いた)
- ClAmP_MainTex0_linear(大文字小文字がぐちゃぐちゃ)
まとめ
SamplerState
が消えてしまう問題をフィルターモードとラップモードを書くことで回避しました。
消えないテクニックが存在するかShaderの書き方が悪いのか分かりませんが現状はこの方法でこの問題を私は乗り越えています。
これをしなくても回避する方法がありましたら教えて頂きたいです。