0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Unity】サーフェスシェーダーで Grab Pass を使うと出るエラーを解消する

Posted at

Unity 2022.3.22f1 における内容です。

概要

サーフェスシェーダーで Grab Pass を使うときに、Unity のマクロ UNITY_DECLARE_SCREENSPACE_TEXTURE を使うとエラーが出ます。マクロを使わず普通に sampler2D で宣言すれば解決するのですが、気持ち悪いので原因と対策を考えます。

問題

サーフェスシェーダーでももちろん Grab Pass は使えます。例えばこんな感じです。

SurfaceGrab.shader
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_TEXTUREHLSLSupport.cginc で定義されています。

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 のほうも変更することにします。)

OverrideUnity.cginc
#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" の直後にインクルードすれば、マクロが書き換えられてコンパイルが通るようになります。

雑記

これは... バグなんでしょうか?あからさまなので意図的なような気がしますが、コンパイルが通らないなら変えるしかないですよね...

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?