Unity 2022.3.22f1 における内容です。
概要
サーフェスシェーダーで Grab Pass を使うときに、Unity のマクロ UNITY_DECLARE_SCREENSPACE_TEXTURE
を使うとエラーが出ます。マクロを使わず普通に sampler2D
で宣言すれば解決するのですが、気持ち悪いので原因と対策を考えます。
問題
サーフェスシェーダーでももちろん Grab Pass は使えます。例えばこんな感じです。
Shader "Test/SurfaceGrab"
{
Properties
{
_Shift ("Shift", Range(-1, 1)) = 0
}
SubShader
{
Tags { "RenderType"="Opaque" "Queue" = "Overlay" }
GrabPass { "_MyGrabTex" }
CGPROGRAM
#pragma surface surf Standard vertex:vert
#pragma multi_compile_instancing
#include "UnityCG.cginc"
// #include "OverrideUnity.cginc" // 後で使います
struct Input
{
float4 grabPos;
UNITY_VERTEX_OUTPUT_STEREO
};
float _Shift;
UNITY_DECLARE_SCREENSPACE_TEXTURE( _MyGrabTex );
void vert ( inout appdata_full v, out Input o )
{
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
// スクリーン座標の計算
o.grabPos = ComputeGrabScreenPos(UnityObjectToClipPos(v.vertex));
}
void surf (Input IN, inout SurfaceOutputStandard o)
{
float2 grabPos = IN.grabPos.xy / IN.grabPos.w;
grabPos.x += _Shift;
float4 grabTex = UNITY_SAMPLE_SCREENSPACE_TEXTURE(_MyGrabTex, grabPos);
o.Albedo = grabTex.rgb;
}
ENDCG
}
}
ただ、これをコンパイルするとこんなエラーが出ます。
Shader error in 'Test/SurfaceGrab': Unexpected token ';'. Expected one of: typedef const void inline uniform nointerpolation extern shared static volatile row_major column_major struct or a user-defined type at line 28
曰く、UNITY_DECLARE_SCREENSPACE_TEXTURE( _MyGrabTex )
の後ろに ;
がついてるのはおかしい、とのことなので、;
を削除してもう一度コンパイルしてみます。
すると、今度はこんなエラーが出ます。
Shader error in 'Test/SurfaceGrab': syntax error: unexpected token 'void' at line 30 (on d3d11)
曰く、UNITY_DECLARE_SCREENSPACE_TEXTURE( _MyGrabTex )
の後ろに ;
がないのはおかしい、とのこと。
うーん、理不尽。
原因
どうやら、シェーダーのバリアントによってエラーの内容が変わっているようなので、UNITY_DECLARE_SCREENSPACE_TEXTURE
がどういうマクロなのかを調べてみます。
UNITY_DECLARE_SCREENSPACE_TEXTURE
は HLSLSupport.cginc
で定義されています。
...
#if defined(UNITY_STEREO_INSTANCING_ENABLED) || defined(UNITY_STEREO_MULTIVIEW_ENABLED)
#undef UNITY_DECLARE_DEPTH_TEXTURE_MS
#define UNITY_DECLARE_DEPTH_TEXTURE_MS(tex) UNITY_DECLARE_TEX2DARRAY_MS (tex)
#undef UNITY_DECLARE_DEPTH_TEXTURE
#define UNITY_DECLARE_DEPTH_TEXTURE(tex) UNITY_DECLARE_TEX2DARRAY (tex)
#undef SAMPLE_DEPTH_TEXTURE
#define SAMPLE_DEPTH_TEXTURE(sampler, uv) UNITY_SAMPLE_TEX2DARRAY(sampler, float3((uv).x, (uv).y, (float)unity_StereoEyeIndex)).r
#undef SAMPLE_DEPTH_TEXTURE_PROJ
#define SAMPLE_DEPTH_TEXTURE_PROJ(sampler, uv) UNITY_SAMPLE_TEX2DARRAY(sampler, float3((uv).x/(uv).w, (uv).y/(uv).w, (float)unity_StereoEyeIndex)).r
#undef SAMPLE_DEPTH_TEXTURE_LOD
#define SAMPLE_DEPTH_TEXTURE_LOD(sampler, uv) UNITY_SAMPLE_TEX2DARRAY_LOD(sampler, float3((uv).xy, (float)unity_StereoEyeIndex), (uv).w).r
#undef SAMPLE_RAW_DEPTH_TEXTURE
#define SAMPLE_RAW_DEPTH_TEXTURE(tex, uv) UNITY_SAMPLE_TEX2DARRAY(tex, float3((uv).xy, (float)unity_StereoEyeIndex))
#undef SAMPLE_RAW_DEPTH_TEXTURE_PROJ
#define SAMPLE_RAW_DEPTH_TEXTURE_PROJ(sampler, uv) UNITY_SAMPLE_TEX2DARRAY(sampler, float3((uv).x/(uv).w, (uv).y/(uv).w, (float)unity_StereoEyeIndex))
#undef SAMPLE_RAW_DEPTH_TEXTURE_LOD
#define SAMPLE_RAW_DEPTH_TEXTURE_LOD(sampler, uv) UNITY_SAMPLE_TEX2DARRAY_LOD(sampler, float3((uv).xy, (float)unity_StereoEyeIndex), (uv).w)
#define UNITY_DECLARE_SCREENSPACE_SHADOWMAP UNITY_DECLARE_TEX2DARRAY
#define UNITY_SAMPLE_SCREEN_SHADOW(tex, uv) UNITY_SAMPLE_TEX2DARRAY( tex, float3((uv).x/(uv).w, (uv).y/(uv).w, (float)unity_StereoEyeIndex) ).r
#define UNITY_DECLARE_SCREENSPACE_TEXTURE UNITY_DECLARE_TEX2DARRAY
#define UNITY_SAMPLE_SCREENSPACE_TEXTURE(tex, uv) UNITY_SAMPLE_TEX2DARRAY(tex, float3((uv).xy, (float)unity_StereoEyeIndex))
#else
#define UNITY_DECLARE_DEPTH_TEXTURE_MS(tex) Texture2DMS<float> tex;
#define UNITY_DECLARE_DEPTH_TEXTURE(tex) sampler2D_float tex
#define UNITY_DECLARE_SCREENSPACE_SHADOWMAP(tex) sampler2D tex
#define UNITY_SAMPLE_SCREEN_SHADOW(tex, uv) tex2Dproj( tex, UNITY_PROJ_COORD(uv) ).r
#define UNITY_DECLARE_SCREENSPACE_TEXTURE(tex) sampler2D_float tex;
#define UNITY_SAMPLE_SCREENSPACE_TEXTURE(tex, uv) tex2D(tex, uv)
#endif
...
#else
の後を見ると、UNITY_DECLARE_SCREENSPACE_TEXTURE
の定義の末尾に ;
がついています。(UNITY_DECLARE_DEPTH_TEXTURE_MS
にもついていますね...)
#if
節のほうは ;
がついていないので、この差が理不尽エラーの原因と考えられます。
対策
HLSLSupport.cginc
を改造するのは面倒なので、UNITY_DECLARE_SCREENSPACE_TEXTURE
マクロだけオーバーライドすることにします。
こんな感じのファイルを作ります。(ついでに UNITY_DECLARE_DEPTH_TEXTURE_MS
のほうも変更することにします。)
#ifndef _OVERRIDE_UNITY_INCLUDED_
#define _OVERRIDE_UNITY_INCLUDED_
#if (!(defined(UNITY_STEREO_INSTANCING_ENABLED) || defined(UNITY_STEREO_MULTIVIEW_ENABLED)))
#undef UNITY_DECLARE_DEPTH_TEXTURE_MS
#define UNITY_DECLARE_DEPTH_TEXTURE_MS(tex) Texture2DMS<float> tex
#undef UNITY_DECLARE_SCREENSPACE_TEXTURE
#define UNITY_DECLARE_SCREENSPACE_TEXTURE(tex) sampler2D_float tex
#endif
#endif // _OVERRIDE_UNITY_INCLUDED_
これを、冒頭の SurfaceGrab.shader
の #include "UnityCG.cginc"
の直後にインクルードすれば、マクロが書き換えられてコンパイルが通るようになります。
雑記
これは... バグなんでしょうか?あからさまなので意図的なような気がしますが、コンパイルが通らないなら変えるしかないですよね...