【Unity】【シェーダ】ディザ抜きで半透明描画を実現する

f:id:halya_11:20180131204054p:plain:w300

今回はディザ抜きと呼ばれる半透明表現をやってみます。

ディザ抜き?

ディザ抜きは、半透明の表現手法ではありますが、不透明のパスで描画します。
不透明で描画したピクセルに対して一定間隔で穴を空けることで、半透明であるかのように見せかけます。

網目のとても細かい網戸のようなものです。

f:id:halya_11:20180130230613j:plain:w300

半透明描画はフィルレートがかさみがちですが、ディザ抜きは不透明描画なので、
上手く使えばパフォーマンスが稼げるというメリットがあります。

ニーアなどでも使われているようです。
https://cgworld.jp/feature/201704-cgw225-nier-3.html

ディザ抜きシェーダ

Shader "DitherTransparent" {
    Properties {
        _MainTex ("Base (RGB)", 2D) = "white" {}
        _DitherTex ("Dither Pattern (R)", 2D) = "white" {}
        _Alpha ("Alpha", Range(0.0, 1.0)) = 1.0
    }
    SubShader {
        Tags { "RenderType"="Opaque" }

        Pass{
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata {
                float4 vertex: POSITION;
                float2 texcoord: TEXCOORD0;
            };

            struct v2f {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                float4 clipPos : TEXCOORD1;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _DitherTex;
            float _Alpha;

            v2f vert (appdata v) {
                v2f o = (v2f)0;
                o.pos = UnityObjectToClipPos(v.vertex);

                // クリップ座標を求める
                o.clipPos = UnityObjectToClipPos(v.vertex);

                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                return o;
            }

            float4 frag (v2f i): COLOR {
                float4 color = tex2D(_MainTex, i.uv);

                // クリップ座標からビューポート座標を求める
                float2 viewPortPos = i.clipPos.xy / i.clipPos.w * 0.5 + 0.5;

                // スクリーン座標を求める
                float2 screenPos = viewPortPos * _ScreenParams.xy;

                // ディザリングテクスチャ用のUVを作成
                float2 ditherUv = screenPos / 4;

                float dither = tex2D(_DitherTex, ditherUv).r;
                clip(_Alpha - dither);

                return color;
            }
            ENDCG
        }
    }
}

_DitherTex というプロパティには、4x4ピクセルの、各ピクセルに様々な値が入ったテクスチャを渡します。

今回使ったのはBayerMatrixというパターンです。
http://fe0km.blog.fc2.com/blog-entry-122.html

フラグメントシェーダで、このテクスチャをスクリーンに対して敷き詰め、
_Alphaプロパティと比較して一部分を clip() しています。

これにより一定間隔でピクセルが破棄され、半透明描画のような効果が生まれました。

f:id:halya_11:20180130232044g:plain