【Unity】【シェーダ】Spiral Blur(渦巻ブラー)のポストエフェクトを実装する

UnityのシェーダでSpiral Blur(渦巻ブラー)のポストエフェクトを実装する方法です。

Unity2019.2.11

はじめに

この記事ではUnityのシェーダで渦巻型にブラーをかけるポストエフェクトを実装する方法をまとめます。
Unityでポストエフェクトを掛ける基本的な方法については以下の記事にまとめていますので、必要に応じて参照してください。

light11.hatenadiary.com

シェーダ

早速ですが、まずシェーダ全文を掲載します。

Shader "Example"
{
    Properties
    {
        [NoScaleOffset] _MainTex ("Texture", 2D) = "white" {}
        _SampleCount ("Sample Count", int) = 5
        _RotateIntensity ("Rotate Intensity", float) = 0.05
        _RadialIntensity("Radial Intensity", Range(0.0, 1.0)) = 0.5
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            int _SampleCount;
            float _RotateIntensity;
            float _RadialIntensity;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                half angleRad = _RotateIntensity / _SampleCount;
                half3 rgb = tex2D(_MainTex, i.uv).rgb;
                // UVを-0.5~0.5に変換
                half2 symmetryUv = i.uv - 0.5;
                // 外側に行くほどこの値が大きくなる(0~0.707)
                half distance = length(symmetryUv);

                half factor = _RadialIntensity / _SampleCount * distance;
                for(int j = 1; j < _SampleCount; j++)
                {
                    // 回転行列を作る
                    half angle = angleRad * j;
                    half angleCos = cos(angle);
                    half angleSin = sin(angle);
                    half2x2 rotateMatrix = half2x2(angleCos, -angleSin, angleSin, angleCos);
                    // 放射状サンプリングのためのオフセット
                    half uvOffset = 1 - factor * j;

                    float2 uv = mul(symmetryUv * uvOffset, rotateMatrix) + 0.5;
                    rgb += tex2D(_MainTex, uv).rgb;
                }
                rgb /= _SampleCount;
                return fixed4(rgb, 1);
            }
            ENDCG
        }
    }
}

実装としては、以下の二つの記事を組み合わせたような感じです。
ちょっとずつテクスチャを回転させながらちょっとずつ内側をサンプリングしています。

light11.hatenadiary.com

light11.hatenadiary.com

レンダリング結果

さてこれをポストエフェクトとしてレンダリングします。
ポストエフェクトを掛けるためのスクリプトは以下のように書きます。

using UnityEngine;

public class Example : MonoBehaviour
{
    [SerializeField]
    private Material _material;
    [SerializeField, Range(1, 20)]
    private int _sampleCount = 5;
    [SerializeField, Range(0.0f, 1.0f)]
    private float _rotateIntensity = 0.05f;
    [SerializeField, Range(0.0f, 1.0f)]
    private float _radialIntensity = 0.5f;

    private void OnRenderImage(RenderTexture source, RenderTexture dest)
    {
        _material.SetFloat("_SampleCount", _sampleCount);
        _material.SetFloat("_RotateIntensity", _rotateIntensity);
        _material.SetFloat("_RadialIntensity", _radialIntensity);
        Graphics.Blit(source, dest, _material);
    }
}

これをカメラにアタッチして実行すると以下の結果が得られます。

f:id:halya_11:20191206013318p:plain
効果なし

f:id:halya_11:20191206013505p:plain
_RotateIntensity : 0.3
_RadialIntensity : 0.5

f:id:halya_11:20191206013552p:plain
_RotateIntensity : 0.75
_RadialIntensity : 0.5

関連

light11.hatenadiary.com

light11.hatenadiary.com