SuiTechLog

Unity,Arduino,RaspberryPiなど、モノづくり系を気ままに書き残すブログ。

Unity シェーダーでスパイクノイズのお勉強


f:id:sui332015:20180211174510g:plainf:id:sui332015:20180211164834g:plain

 

ひさしぶりのUnityシェーダーのお勉強をいたしました。

スパイクノイズは以下のサイトを参考制作させていただきました。

 

nn-hokuson.hatenablog.com

 

 詳しい説明は上記のサイトを参考にしていただくとして

ノイズの周波数、幅や影響範囲を、位置を自由に変更できるようにする、というのが今回の試みです。

 

上記サイトのuvオフセットの計算式の意味から。。


uv.x += _Amount*sin(10*x)*(-(x-1)*(x-1)+1)

この式は2つに分離できます

 

_Amount*sin(10*x)と (-(x-1)*(x-1)+1);です。

f:id:sui332015:20180211171230j:plain

図1:

 

 前者はただのサイン関数(図1の上)で、永遠に続く波形を作っています。

後者は-1かける(x-1)の二乗+1ということで

二次関数(図2の左下)です。これをそれぞれかけると

サイン関数の波の高さが山なりになる(図1右下)、という寸法です。

 

 左下で二次関数を使っていますが、図1をみていただくとわかるとおり、Y軸が0以下になったあとはひたすら下に落ちていきます。なので、結果のサイン関数は、一定の範囲を超えると逆に永遠に大きくなっていきます。このままオフセットするときはきちんとマイナスになる部分を除外しないといけません。

 

 私はここで二次関数ではなく、ガウス関数を使ってみました。ガウス関数のグラフは特定の場所は山なりになっているけれど、それ以外は0と、データを切り取るときに非常に使える形状になっています。(図2の左下)このガウス関数を図でいうと左右にオフセットさせたり、幅をせまくしたりすることで結果のグラフを自在にきりとれるのです。

f:id:sui332015:20180211171844j:plain

図2:

 

 なお、こういった切り取る関数の事を窓関数というようです。これは、数値分析など幅広い分野で応用されているようです。(ただし、二次関数なんかよりはるかに重い処理なので、使いどころに注意です。ここではピクセルシェーダに使ってしまいましたが、ほんまは重さ的にはよくないはずです。)

 

計算式です、ガウス関数です。サイン関数にこれをかけます。


exp(-(pow(2*x-_offset,2) / pow(_Width,2)));

expは自然指数関数。定数e(2.7....)のほにゃらら乗を表します

powは累乗を表します。

つまり文字に書くと

eの(-1×(2×x-_offset)の二乗)/_Widthの二乗))乗

ということになります。

 

ソースコードは以下です。


Shader "Custom/PulseNoise_sui"
{
	Properties
	{
		_MainTex("Sprite Texture", 2D) = "white" {}	//べーステクスチャ
		_Amount("Distort Amount", Float) = 0.0		//変化量
		_Freq("Frequency", Float) = 10.0			//周波数
		_Width("Width", float) = 0.2				//ゆがみのはば
		_offset("Offset",float) = 0.0				//オフセット
		_boffset("BaseOffset",float) = 0.0			//ベース絵のオフセット

	}
	SubShader
	{
		Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" }	//キューもタイプも透明だよ
		LOD 100

		Blend SrcAlpha OneMinusSrcAlpha	//透明だよ

		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;
			float4 _MainTex_ST;
			float  _Amount;
			float _offset;
			float _Freq;
			float _Width;
			float _boffset;

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

			fixed4 frag(v2f i) : SV_Target
			{
				float2 uv = i.uv;
				float x = uv.y;
				uv.x = i.uv.x + _Amount*sin(_Freq * x) * exp(-(pow(2*x-_offset,2) / pow(_Width,2)));	//Xオフセット。キモ。でもガウス窓つかってるので比較的重いのよ。
				uv.y = i.uv.y+_boffset;	//ベースのYオフセットをかけるよ

				fixed4 col = tex2D(_MainTex, uv);
				return col;
			}
			ENDCG
		}
	}
}    

 というわけで、これをマテリアルに与えて

そこにアニメーションキーをうったのが以下のものです

f:id:sui332015:20180211174510g:plainf:id:sui332015:20180211164834g:plain