【UnityShader】ガウシアンブラー【1】 #64
前回の成果
CommandBufferを使用して、色を反転させた。
今回やること
今回はガウシアンブラーを使用して、ブラーをしていこうと思います。
参考サイト様
事前準備
まず、Scene上にQuadを配置して適当なスケールにします。
そして、QuadのMeshRendererのElement0にデフォルトのUnlitShaderで制作したMaterialをアタッチします。
最後に、今回制作するScriptをアタッチします。
Textureには以下を、
Shaderには今回制作するShaderをアタッチしてください。
念の為、UnlitShaderを貼っておきます。
ソースコードは何も触っていないです。
UnlitShader
Shader "Unlit/GaussianBlurUnlit" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag // make fog work #pragma multi_compile_fog #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; UNITY_FOG_COORDS(1) float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); UNITY_TRANSFER_FOG(o,o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { // sample the texture fixed4 col = tex2D(_MainTex, i.uv); // apply fog UNITY_APPLY_FOG(i.fogCoord, col); return col; } ENDCG } } }
ソースコード
using UnityEngine; public class GaussianBlur : MonoBehaviour { [SerializeField] private Texture _texture; [SerializeField] private Shader _shader; [SerializeField, Range(1f, 10f)] private float _offset = 1f; [SerializeField, Range(10f, 1000f)] private float _blur = 10f; private Material _material; private Renderer _renderer; private RenderTexture _rt1; private RenderTexture _rt2; private float[] _weights = new float[10]; private bool _isInitialized = false; private void Awake() { Initialize(); } private void OnValidate() { if (!Application.isPlaying) { return; } UpdateWeights(); Blur(); } private void Initialize() { if (_isInitialized) { return; } _material = new Material(_shader); _material.hideFlags = HideFlags.HideAndDontSave; _rt1 = RenderTexture.GetTemporary(_texture.width / 2, _texture.height / 2, 0, RenderTextureFormat.ARGB32); _rt2 = RenderTexture.GetTemporary(_texture.width / 2, _texture.height / 2, 0, RenderTextureFormat.ARGB32); _renderer = GetComponent<Renderer>(); UpdateWeights(); _isInitialized = true; } public void Blur() { if (!_isInitialized) { Initialize(); } Graphics.Blit(_texture, _rt1); _material.SetFloatArray("_Weights", _weights); float x = _offset / _rt1.width; float y = _offset / _rt1.height; _material.SetVector("_Offset", new Vector4(x, 0, 0, 0)); Graphics.Blit(_rt1, _rt2, _material); _material.SetVector("_Offset", new Vector4(0, y, 0, 0)); Graphics.Blit(_rt2, _rt1, _material); _renderer.material.mainTexture = _rt1; } private void UpdateWeights() { float total = 0; float d = _blur * _blur * 0.001f; for (int i = 0; i < _weights.Length; i++) { float x = 1.0f + i * 2f; float w = Mathf.Exp(-0.5f * (x * x) / d); _weights[i] = w; if (i > 0) { w *= 2.0f; } total += w; } for (int i = 0; i < _weights.Length; i++) { _weights[i] /= total; } } private void OnDestroy() { RenderTexture.ReleaseTemporary(_rt1); RenderTexture.ReleaseTemporary(_rt2); } }
Shader
Shader "Unlit/GaussianBlur" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma fragmentoption ARB_precision_hint_fastest #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; sampler2D _MainTex; half4 _Offset; static const int samplingCount = 10; half _Weights[samplingCount]; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv; return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = 0; [unroll] for (int j = samplingCount - 1; j > 0; j--) { col += tex2D(_MainTex, i.uv - (_Offset.xy * j)) * _Weights[j]; } [unroll] for (int j = 0; j < samplingCount; j++) { col += tex2D(_MainTex, i.uv + (_Offset.xy * j)) * _Weights[j]; } return col; } ENDCG } } }
ガウシアンブラーとは
画像処理において、ガウシアンぼかし (ガウスぼかし、ガウシアンブラー、ガウシアンフィルター、ガウスフィルター、Gaussian Blur)とは、ガウス関数をもちいて画像をぼかす処理である。
デジタルカメラの撮像画像などからノイズを除去したり、アンシャープマスク処理、エッジ抽出の前処理などに応用できる。ガウシアンぼかし - Wikipedia:より引用
簡単に説明すると、ガウス関数で画像をぼかす処理のことです。
以下の画像を見ていただくとわかりやすいかと思います。
Extending Unity 5 rendering pipeline: Command Buffers - Unity Technologies Blog:より引用
ガウス関数
ガウシアンブラーは、ガウス関数で画像をぼかす処理と前述しました。
では、ガウス関数について説明します。
ガウス関数は以下の式になります。
グラフは以下のような釣り鐘のような形になります。
定義されている数を変化させることで、ガウス関数がどのように変化するか見ていきます。
数値を変化させてグラフの変化を見る
σを変化
- 黄色 : σ = 1
- 緑色 : σ = 0.1
- 青色 : σ = 0.05
σを変化させると、グラフの傾斜が大きくなることがわかります。
μを変化
- 黄色 : μ = 0.5
- 緑色 : μ = 0
- 青色 : μ = -0.5
μを変化させると、グラフの傾斜はそのままで横にズレることがわかります。
今回利用する形で変化
今回は、中央がx=0でyの最大値が1となるガウス関数を使用します。
ですので、定義は以下となります。
元のガウス関数と比べてみるとわかりやすいと思います。
こちらの数式のσの値を変えると変化は以下となります。
- 黄色 : σ = 0.1
- 緑色 : σ = 0.2
- 青色 : σ = 0.3
中心がx=0で、yの最大値が1になっているのがわかります。
ガウス関数でブラーする
ガウス関数の数式を変化させると、グラフがどのように変化するかが理解できました。
では、これを実際にブラーにどう落とし込むかの説明に移ります。
グラフのxをサンプリング点からの距離、yを重みとして扱います。
例として、-0.2,-0.1,0,0.1,0.2の5点を取ってみて重みをみてみようと思います。
各点での重み
-0.2~0.2までの重みは以下となります。
重みの精確さは余り重要ではないので、だいたいの値とします。
重み
図での表記 | x | 重み |
---|---|---|
A | -0.2 | 0.35 |
B | -0.1 | 0.78 |
C | 0 | 1 |
D | 0.1 | 0.78 |
E | 0.2 | 0.35 |
ただし、今回使用する重みはピクセルの色に乗算するものになります。
ですので、重みの合計を1にする必要があります。
正規化した重み
図での表記 | x | 重み |
---|---|---|
A | -0.2 | 0.1 |
B | -0.1 | 0.24 |
C | 0 | 0.3 |
D | 0.1 | 0.24 |
E | 0.2 | 0.1 |
これで各点の重みを合計した値が1となりました。
この値をピクセルの色に乗算してあげることでブラーがかかります。
ガウス関数を使う利点
ガウス関数でのxyが何を表すのかが理解できたと思います。
ガウス関数の特徴である、3次元で用いる場合にxとyを切り離して処理できることにあります。
切り離せない場合ですと、各テクセルに対して処理を行わないといけません。
ですので、仮に5x5のテクスチャだった場合に25回処理を行う必要があります。
しかし、切り離せる場合はxとyを分けて処理ができます。
なので、5+5で10回ですみます。
これが仮に100x100だとすると、10000回と200回でかなり差が出てしまいます。
ですので、処理回数を抑えるためにガウス関数を使用します。
今回は以上となります。
ここまでご視聴ありがとうございました。