Unityブラーシェーダー

Unityブラーシェーダー

[Unity uGUI] ブラーシェーダー(Blur Shader)。
画像をブレさせる処理。

関連ページ
シェーダー処理の中でも一般的な部類のBlurシェーダーを作ってみました。画像をブレさせ、ぼやけさせる処理です。

Blurシェーダー概要

下がブラーシェーダーを効果を撮った動画です。
プロパティの
BlurCount
がBlurを掛ける回数、
BlurDistance
がBlurの制度になります。


BlurCountは、1上げる度に爆発的にBlurのサンプリング回数が上がっていきます。
たとえばこのシェーダーで設定している最大値の20を指定すると、(20x2+1)の2乗で画像1ピクセルにつき1681回テクスチャを読み込みます。
画像のぼやけ具合を調整する際は、BlurCountは適度に抑えて、なるべくBlurDistanceで調整する方法をお勧めします。

コード全文

以下シェーダーのコード全文です。uGUI Imageへの反映方法は こちらの記事 参照してください。
基礎知識として次の記事にも目を通すと分かり易いです。 p.57 : シェーダー基礎、最もシンプルなuGUI用 URP Shader
Shader "UI/BoxBlur"
{
    Properties 
    {
        [HideInInspector]_MainTex("-",2D)="white"{} 
        _BlurCount("Blur Count", Range(0, 20)) = 1 
//何回Blur処理を繰り返すか 

        _BlurDistance("Blur Distance", Range(1, 10)) = 1 
//Blurの際、何ピクセルずれた場所からピクセル色を抜き出すか

    }

    SubShader
    {
        Tags { "Queue" = "Transparent" }
        Cull Off
        ZWrite Off
        Blend SrcAlpha OneMinusSrcAlpha

        HLSLINCLUDE      
            #pragma vertex vert
            #pragma fragment frag
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            
//uGUI Image > Source Imageにアタッチされた画像を参照できるようにする

            TEXTURE2D(_MainTex);        
            SAMPLER(sampler_MainTex);            
            
//Source Imageにアタッチされた画像のピクセルサイズを参照できるようにする

            float4 _MainTex_TexelSize;

            
//SRP Batcherを適用

            CBUFFER_START(UnityPerMaterial)            
                
//今回追加したプロパティに対応する変数

                int _BlurCount;
                half _BlurDistance;                
            CBUFFER_END
            
            struct Attributes
            {
                half2 uv : TEXCOORD0;
                float4 positionOS : POSITION;
                half4 color : COLOR;
            };

            struct Varyings
            {
                half2 uv : TEXCOORD0;
                float4 positionCS : SV_POSITION;
                half4 color : COLOR;
            };

            Varyings vert (Attributes input)
            {
                Varyings output;
                output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
                output.uv = input.uv;
                output.color = input.color;
                return output;
            }
        ENDHLSL    

        Pass
        {
            HLSLPROGRAM
            half4 frag (Varyings input) : SV_Target
            {
                
//Source Imageにアタッチされた画像の縦横ピクセルサイズに対し、その逆数を取得

                
//x: 1/width(ピクセル幅の逆数)

                
//y: 1/height(ピクセル高さの逆数)

                
//z: width(ピクセル幅そのもの)

                
//w: height(ピクセル高さそのもの)

                half2 texelSize = _MainTex_TexelSize.xy;

                half4 color = 0;
                int count = 0;
                for (int x = -_BlurCount; x <= _BlurCount; x++)  
                {  
                    for (int y = -_BlurCount; y <= _BlurCount; y++)  
                    {                      
                        
//x軸、y軸に対しuv座標をいくらずらすか取得

                        half2 offset = half2(x * _BlurDistance, y * _BlurDistance) * texelSize;

                        
//inputから渡されたuv座標に、offsetの修正値を与えて指定画像からピクセル色を取得してcolorに色を加算

                        color += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv + offset); 
                        count++;
                    }               
                }  

                
//加算した回数分だけ割って平均色を出す

                return color / count;
            }
            ENDHLSL
        }
    }
}

コード解説

コードの中でいまいち分かり辛い部分は、
_MainTex_TexelSize.xy
という画像サイズの逆数を計算に使っている部分になります。
                        
//Source Imageにアタッチされた画像の縦横ピクセルサイズに対し、その逆数を取得

                        
//x: 1/width(ピクセル幅の逆数)

                        
//y: 1/height(ピクセル高さの逆数)

                        
//z: width(ピクセル幅そのもの)

                        
//w: height(ピクセル高さそのもの)

                        half2 texelSize = _MainTex_TexelSize.xy;

逆数を使っているのは、シェーダーのuv座標というものが、0~1の間に値が正規化されているためです。
5x5ピクセルの極小の画像があるとして、そのuv座標は1 / 5 = 0.2ずつ、紐づくピクセルが移動していきます。
左端のピクセルはx = 0~0.2の範囲、右端のピクセルは0.8~1の範囲です。

500x500ピクセルの場合、そのuv座標は1 / 500 = 0.002ずつ移動していきます。
左端のピクセルはx = 0~0.002の範囲、右端のピクセルは0.008~1の範囲です。
画像サイズは大きく違いますが、uv座標は変わらず0~1の範囲です。

_BlurCount = 1の時、for文を見るとx軸y軸ともに3回ずつBlur処理を行っています。
                for (int x = -_BlurCount; x <= _BlurCount; x++)  
                {  
                    for (int y = -_BlurCount; y <= _BlurCount; y++)  
                    {                      
                        
//x軸、y軸に対しuv座標をいくらずらすか取得

                        half2 offset = half2(x * _BlurDistance, y * _BlurDistance) * texelSize;
xもyも-1, 0, 1のいずれかの値で、(x = 0, y = 0)は座標をずらさないオリジナルの画像です。
そしてtexelSizeは1ピクセル分の座標の移動値を表します。
つまり_BlurDistance = 1だとした場合、上下左右に1ピクセルだけブレさせて重ねる処理という事が分かります。
0
0