【UnityShader】ノイズシェーダー拡張 【1】#44
前回の成果
フローマップを制作した。
今回やること
ノイズシェーダーの拡張を行っていきます。
cginc
#ifndef EXTENSION_NOISE_UTIL #define EXTENSION_NOISE_UTIL #include "UnityCG.cginc" fixed2 TRANSFORM_NOISE_TEX(fixed2 uv, fixed4 tilingOffset, fixed4 sizeScroll) { uv = uv * tilingOffset.xy * sizeScroll.xy + tilingOffset.zw; uv += fixed2(sizeScroll.z * tilingOffset.x, -sizeScroll.w * tilingOffset.y) * _Time.y; return uv; } fixed rand(fixed2 uv, fixed2 size) { uv = frac(uv / size); return frac(sin(dot(frac(uv / size), fixed2(12.9898, 78.233))) * 43758.5453) * 0.99999; } fixed2 gradientVector(fixed2 uv, fixed2 size) { uv = frac(uv / size); uv = fixed2(dot(frac(uv / size), fixed2(127.1, 311.7)), dot(frac(uv / size), fixed2(269.5, 183.3))); return -1.0 + 2.0 * frac(sin(uv) * 43758.5453123); } fixed2 bilinear(fixed f0, fixed f1, fixed f2, fixed f3, fixed fx, fixed fy) { return lerp(lerp(f0, f1, fx), lerp(f2, f3, fx), fy); } fixed fade(fixed t) { return t * t * t * (t * (t * 6.0 - 15.0) + 10.0); } fixed blockNoise(fixed2 uv, fixed2 size) { return rand(floor(uv), size); } fixed valueNoiseOne(fixed2 uv, fixed2 size) { fixed2 p = floor(uv); fixed2 f = frac(uv); float f00 = rand(p + fixed2(0, 0), size); float f10 = rand(p + fixed2(1, 0), size); float f01 = rand(p + fixed2(0, 1), size); float f11 = rand(p + fixed2(1, 1), size); return bilinear( f00, f10, f01, f11, fade(f.x), fade(f.y) ); } fixed valueNoise(fixed2 uv, fixed2 size) { fixed f = 0; f += valueNoiseOne(uv * 2, size) / 2; f += valueNoiseOne(uv * 4, size) / 4; f += valueNoiseOne(uv * 8, size) / 8; f += valueNoiseOne(uv * 16, size) / 16; f += valueNoiseOne(uv * 32, size) / 32; return f; } fixed perlinNoiseOne(fixed2 uv, fixed2 size) { fixed2 p = floor(uv); fixed2 f = frac(uv); fixed d00 = dot(gradientVector(p + fixed2(0, 0), size), f - fixed2(0, 0)); fixed d10 = dot(gradientVector(p + fixed2(1, 0), size), f - fixed2(1, 0)); fixed d01 = dot(gradientVector(p + fixed2(0, 1), size), f - fixed2(0, 1)); fixed d11 = dot(gradientVector(p + fixed2(1, 1), size), f - fixed2(1, 1)); return bilinear(d00, d10, d01, d11, fade(f.x), fade(f.y)) + 0.5f; } fixed perlinNoise(fixed2 uv, fixed2 size) { fixed f = 0; f += perlinNoiseOne(uv * 2, size) / 2; f += perlinNoiseOne(uv * 4, size) / 4; f += perlinNoiseOne(uv * 8, size) / 8; f += perlinNoiseOne(uv * 16, size) / 16; f += perlinNoiseOne(uv * 32, size) / 32; return f; } fixed3 normalNoise(fixed2 uv, fixed2 size) { fixed3 result = fixed3(perlinNoise(uv.xy, size), perlinNoise(uv.xy + fixed2(1, 1), size), 1.0); return result; } #endif
まずは、下記サイト様のcgincから理解しようと思います。
cginc
これはUnityのShaderのincludeファイルとなっています。
よく使用される例としてUnityCG.cgincがあります。
以下のようにすることにより、二重インクルードを防止することができます。
#ifndef HOGE_INCLUDED #define HOGE_INCLUDED // 処理 #endif
TRANSFORM_NOISE_TEX
fixed2 TRANSFORM_NOISE_TEX(fixed2 uv, fixed4 tilingOffset, fixed4 sizeScroll) {
uv = uv * tilingOffset.xy * sizeScroll.xy + tilingOffset.zw;
uv += fixed2(sizeScroll.z * tilingOffset.x, -sizeScroll.w * tilingOffset.y) * _Time.y;
return uv;
}
この関数で、ノイズシェーダー用のuv値を計算しています。
TRANSFORM_TEXのノイズ版だと思って頂ければイメージしやすいと思います。
この関数では_MainTexを考慮していないのでそこだけ注意してください。
引数
引数名 | 型 | 格納する値 |
---|---|---|
uv | fixed2 | uv値 |
tilingOffset | fixed4 | タイリング(x, y) / オフセット(z, w) |
sizeScroll | fixed4 | 仮想のテクスチャサイズ(x, y ) / UVスクロール(z, w) |
解説
以下でタイリング、オフセット、仮想のテクスチャサイズを考慮したuv値を計算しています。
uv = uv * tilingOffset.xy * sizeScroll.xy + tilingOffset.zw;
そして、以下でUVスクロールを行っています。
タイリングに合わせた相対速度でスクロールをさせたいため乗算しています。
uv += fixed2(sizeScroll.z * tilingOffset.x, -sizeScroll.w * tilingOffset.y) * _Time.y;
試しに実装してみる
このようなソースコードを用意して、
Shader "Unlit/SampleTilingNoise" { Properties { _NoiseTilingOffset("NoiseTex Tiling(x, y) / Offset(z, w)", Vector) = (0.1, 0.1, 0.0) _NoiseSizeScroll("NoiseTex Size(x, y) / Scroll(z, w)", Vector) = (16, 16, 0, 0) } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "extensionNoiseUtil.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; fixed4 _NoiseTilingOffset; fixed4 _NoiseSizeScroll; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_NOISE_TEX(v.uv, _NoiseTilingOffset, _NoiseSizeScroll); return o; } fixed4 frag (v2f i) : SV_Target { fixed p = perlinNoise(i.uv, _NoiseSizeScroll.xy); return fixed4(p, p, p, 1); } ENDCG } } }
※ perlinNoise関数に関しては後ほど説明します。
適当なPlaneを配置して、プロパティを以下の画像のように設定します。
そうすると、タイリングとUVスクロールが出来ているのがわかります。
rand
fixed rand(fixed2 uv, fixed2 size) { uv = frac(uv / size); return frac(sin(dot(frac(uv / size), fixed2(12.9898, 78.233))) * 43758.5453) * 0.99999; }
この関数で擬似的な乱数を生成しています。
乱数は以前やったのでこちらを参照してください。
uv(UV値)をsize(仮想テクスチャサイズ)で除算することで、タイリングの値で繰り返すUV値にしています。
また、fracを使用することにより0.0~1.0を返すようにしています。
結果に0.99999を乗算しているのは、不動小数点の計算の誤差で1を超えることがあるからです。
blockNoise, valueNoiseOne
ブロックノイズとバリューノイズです。
こちらも以前行ったので詳しくはそちらを参考にしてください。
soramamenatan.hatenablog.com
SampleTilingNoise.shaderのperlinNoiseをblockNoiseやvalueNoiseOneに置き換えると実装できます。
fixed4 frag (v2f i) : SV_Target { // ここを置き換える fixed p = perlinNoise(i.uv, _NoiseSizeScroll.xy); return fixed4(p, p, p, 1); }
ブロックノイズ
バリューノイズ
valueNoise
uvが異なるノイズを加算しています。
今回のケースですと、5枚分加算しています。
加算することにより、ノイズの強度が上がります。
バリューノイズ1枚
バリューノイズ5枚加算
今回は以上となります。
次回はパーリンノイズについて勉強します。