memomem

備忘録・メモ置き場

Unity Shader 半透明ディザリング, ライト方向

[Unity5]破綻しない半透明描画を行う | notargs.com

ディザリングはUnity5標準の影の描画でも使われているため、デフォルトで「_DitherMaskLOD」という名前の3Dテクスチャが定義されています。

x/yにスクリーン座標、zにアルファ値を指定することで、そのピクセルが透明かどうか(透明なら0、違えば1)を取得できます。

これをそのままclip関数へ渡してやれば、ディザリングを用いた描画の実装は完了です。

シェーダで面を円形に切り抜きたかったため、ShadowCasterパスを定義し直しています。

UnityStandardShadow.cgincをinclude、標準のvertShadowCaster/fragShadowCasterの代わりに自前で定義したfrag/vert関数を使い、その中で切り抜きを行っています。

Shader "MyTransparent"
{
    Properties
    {
        _Color("Color", Color) = (1,1,1,1)
        _Glossiness("Smoothness", Range(0.0, 1.0)) = 0.5
        [Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0
        [HideInInspector] _MainTex("Texture", 2D) = "white" {}
    }
    SubShader{

        //============================
        // レンダリング用のPass
        CGPROGRAM
        #pragma surface surf Standard

        half4 _Color;
        sampler3D   _DitherMaskLOD;

        // 入力構造体
        struct Input {
            float2 uv_MainTex;
            float3 worldPos;
            float4 screenPos;
        };

        void surf(Input IN, inout SurfaceOutputStandard o) {

            // ディザリングで半透明を表現
            half alphaRef = tex3D(_DitherMaskLOD, float3(IN.screenPos.xy / IN.screenPos.w * _ScreenParams.xy * 0.25, _Color.a*0.9375)).a;
            clip(alphaRef - 0.01);

            // 円形に切り抜く
            clip(0.5 - length(IN.uv_MainTex - 0.5));

            // 色
            o.Albedo = _Color.rgb;
            o.Alpha = _Color.a;
        }
        ENDCG

        //============================
        // 影の判定を行うPass
        Pass{
            Name "ShadowCaster"
            Tags{ "LightMode" = "ShadowCaster" }

            ZWrite On ZTest LEqual

            CGPROGRAM
            #pragma target 3.0

            #pragma vertex vert
            #pragma fragment frag

            #define UNITY_STANDARD_USE_SHADOW_OUTPUT_STRUCT
            #define UNITY_STANDARD_USE_DITHER_MASK
            #define UNITY_STANDARD_USE_SHADOW_UVS

            #include "UnityStandardShadow.cginc"

            // 頂点シェーダ出力構造体
            struct VertexOutput
            {
                V2F_SHADOW_CASTER_NOPOS
                float2 tex : TEXCOORD1;
            };

            // 頂点シェーダ
            void vert(VertexInput v, out VertexOutput o, out float4 opos : SV_POSITION)
            {
                TRANSFER_SHADOW_CASTER_NOPOS(o,opos)
                o.tex = v.uv0;
            }

            // フラグメントシェーダ
            half4 frag(VertexOutput i, UNITY_VPOS_TYPE vpos : VPOS) : SV_Target
            {
                // ディザリングで半透明を表現
                half alphaRef = tex3D(_DitherMaskLOD, float3(vpos.xy*0.25, _Color.a*0.9375)).a;
                clip(alphaRef - 0.01);

                // 円形に切り抜く
                clip(0.5 -length(i.tex - 0.5));

                SHADOW_CASTER_FRAGMENT(i)
            }

            ENDCG
        }
    }

    FallBack "Differd"
}

Shader "Unlit/Screen Position"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 3.0

            // この構造体に SV_POSITION がないことに注意
            struct v2f {
                float2 uv : TEXCOORD0;
            };

            v2f vert (
                float4 vertex : POSITION, // 頂点位置の入力
                float2 uv : TEXCOORD0, // テクスチャ座標の入力
                out float4 outpos : SV_POSITION // クリップスペース位置の出力
                )
            {
                v2f o;
                o.uv = uv;
                outpos = UnityObjectToClipPos(vertex);
                return o;
            }

            sampler2D _MainTex;

            fixed4 frag (v2f i, UNITY_VPOS_TYPE screenPos : VPOS) : SV_Target
            {
                // screenPos.xy はピクセルの整数の座標を含みます
                // それを使用して、4×4 のピクセルの
                // レンダリングを行わない碁盤模様を実装します

                // 碁盤模様の 4x4 のピクセルの
                // checker 値は負の値です
                screenPos.xy = floor(screenPos.xy * 0.25) * 0.5;
                float checker = -frac(screenPos.r + screenPos.g);

                // 値が負の場合、HLSL の clip はレンダリングを行いません
                clip(checker);

                // 維持されたピクセルが、テクスチャを読み込み、それを出力します
                fixed4 c = tex2D (_MainTex, i.uv);
                return c;
            }
            ENDCG
        }
    }
}

docs.unity3d.com

light11.hatenadiary.com

baba-s.hatenablog.com

www.shibuya24.info