UnityのシェーダでSpiral Blur(渦巻ブラー)のポストエフェクトを実装する方法です。
Unity2019.2.11
はじめに
この記事ではUnityのシェーダで渦巻型にブラーをかけるポストエフェクトを実装する方法をまとめます。
Unityでポストエフェクトを掛ける基本的な方法については以下の記事にまとめていますので、必要に応じて参照してください。
シェーダ
早速ですが、まずシェーダ全文を掲載します。
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 } } }
実装としては、以下の二つの記事を組み合わせたような感じです。
ちょっとずつテクスチャを回転させながらちょっとずつ内側をサンプリングしています。
レンダリング結果
さてこれをポストエフェクトとしてレンダリングします。
ポストエフェクトを掛けるためのスクリプトは以下のように書きます。
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); } }
これをカメラにアタッチして実行すると以下の結果が得られます。
効果なし
_RotateIntensity : 0.3
_RadialIntensity : 0.5
_RotateIntensity : 0.75
_RadialIntensity : 0.5