知識0からのUnityShader勉強

知識0からのUnityShader勉強

UnityのShaderをメインとして、0から学んでいくブログです。

【UnityShader】ノイズシェーダー拡張 【1】#44

前回の成果

フローマップを制作した。

soramamenatan.hatenablog.com


今回やること

ノイズシェーダーの拡張を行っていきます。

sasanon.hatenablog.jp


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から理解しようと思います。

sasanon.hatenablog.jp


cginc

これはUnityのShaderのincludeファイルとなっています。
よく使用される例としてUnityCG.cgincがあります。

以下のようにすることにより、二重インクルードを防止することができます。

#ifndef HOGE_INCLUDED
#define HOGE_INCLUDED

// 処理

#endif

light11.hatenadiary.com


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を配置して、プロパティを以下の画像のように設定します。

f:id:soramamenatan:20200314114635p:plain

そうすると、タイリングとUVスクロールが出来ているのがわかります。

f:id:soramamenatan:20200314114938g:plain


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;
}

この関数で擬似的な乱数を生成しています。
乱数は以前やったのでこちらを参照してください。

soramamenatan.hatenablog.com

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);
}
ブロックノイズ

f:id:soramamenatan:20200314194559p:plain

バリューノイズ

f:id:soramamenatan:20200314194739p:plain


valueNoise

uvが異なるノイズを加算しています。
今回のケースですと、5枚分加算しています。
加算することにより、ノイズの強度が上がります。

バリューノイズ1枚

f:id:soramamenatan:20200314194739p:plain

バリューノイズ5枚加算

f:id:soramamenatan:20200314195752p:plain

今回は以上となります。
次回はパーリンノイズについて勉強します。