Noise

canvasに対応していません。

■ノイズの種類
OFF 
ホワイトノイズ  ブロックノイズ(fBm) 
バリューノイズ(fBm)  パーリンノイズ(fBm) 
セルラーノイズ  セルラーノイズ反転 
セルラーノイズ(タイル分割)  セルラーノイズ反転(タイル分割) 

■fBm反復回数


■アニメーション

説明

ノイズテクスチャをオフスクリーンで生成して、レイマーチングの距離関数にマッピングさせてみました。
ここで紹介しているのはベーシックなノイズ生成手法のみですが、更に調べていくと頭のおかしい手法がまだまだたくさんあって面白い、というか発狂しそうです。
情報源としてはこちらこちらのサイトが有名です。いつも勉強させていただいてます。

補助関数

ノイズの生成に使用する関数をいくつか準備しておきます。
// vec2の入力から0.0~1.0の乱数を出力する float random(vec2 v) { // この実装が伝統的によく使われる // ただし、乱数精度はあまり高くない return fract(sin(dot(v, vec2(12.9898, 78.233))) * 43758.5453); // 改良版としてよくみかけるのが、Dave_Hoskins氏のハッシュ関数(MITライセンス) // https://www.shadertoy.com/view/4djSRW // sin関数を使用しないため、精度を高く保つことができるらしい // 注意として、"This is for integer scaled floats only!"とコメントにあるように大きな値を渡す必要がある // 0.0~1.0を渡すような場合は、0.0~1500.0(解像度分くらい?)にスケールさせてから渡す } // 値を2つ出力するバージョン vec2 random2(vec2 v) { return vec2(random(v), random(v + vec2(10.0f, 10.0f))); } // 0.0~1.0の周期的なsin波アニメーションを返す // ノイズをうねうねさせたかったので、これを挟んでいる float animation(float f) { float speed = 2.0; return sin(f * 6.283 + u_time * speed) * 0.5 + 0.5; } // 値を2つ受け取り2つ出力するバージョン vec2 animation2(vec2 v) { return vec2(animation(v.x), animation(v.y)); } // 0.0~1.0のイーズインアウトな補間式 float interpolation(float f) { // smoothstepの内部でも使われている式 // return f * f * (3.0 - 2.0 * f); // こちらの方が最初の入り方と最後の抜け方がやさしい // 結果、ノイズにアーティファクトがのりづらい return f * f * f * (f * (6.0 * f - 15.0) + 10.0); }


ホワイトノイズ(White Noise)

全ての点においてランダムな値を返します。
float noise_(vec2 uv) { float o = animation(random(uv)); return o; }


ブロックノイズ(Block Noise)

一定間隔に区切られた矩形の範囲毎に同じランダム値を返します。
float noise_(vec2 uv) { uv *= 8.0; vec2 i_uv = floor(uv); float o = animation(random(i_uv)); return o; }


バリューノイズ(Value Noise)

ブロックノイズの4点間を補間して滑らかにしたもの。
float noise_(vec2 uv) { uv *= 8.0; vec2 i_uv = floor(uv); vec2 f_uv = fract(uv); float f1 = animation(random(i_uv + vec2(0.0, 0.0))); float f2 = animation(random(i_uv + vec2(1.0, 0.0))); float f3 = animation(random(i_uv + vec2(0.0, 1.0))); float f4 = animation(random(i_uv + vec2(1.0, 1.0))); float o = mix( mix(f1, f2, interpolation(f_uv.x)), mix(f3, f4, interpolation(f_uv.x)), interpolation(f_uv.y)); return o; }


パーリンノイズ(Perlin Noise)

クラシックなパーリンノイズの実装です。
バリューノイズでは4点の単一値を補間しましたが、パーリンノイズでは4点に勾配ベクトルを設定し、計算する点へのベクトルとの内積をとることで影響値を算出します。
バリューノイズに比べ、ブロック状のアーティファクトが目立たずより自然な見た目になります。
float noise_(vec2 uv) { uv *= 8.0; vec2 i_uv = floor(uv); vec2 f_uv = fract(uv); vec2 v1 = animation2(random2(i_uv + vec2(0.0, 0.0))) * 2.0 - 1.0; vec2 v2 = animation2(random2(i_uv + vec2(1.0, 0.0))) * 2.0 - 1.0; vec2 v3 = animation2(random2(i_uv + vec2(0.0, 1.0))) * 2.0 - 1.0; vec2 v4 = animation2(random2(i_uv + vec2(1.0, 1.0))) * 2.0 - 1.0; float o = mix( mix(dot(v1, f_uv - vec2(0.0, 0.0)), dot(v2, f_uv - vec2(1.0, 0.0)), interpolation(f_uv.x)), mix(dot(v3, f_uv - vec2(0.0, 1.0)), dot(v4, f_uv - vec2(1.0, 1.0)), interpolation(f_uv.x)), interpolation(f_uv.y)) * 0.5 + 0.5; return o; }
ここでは実装していませんが、他にも改良版の手法が考案されています。
改良版パーリンノイズ …… 完全にランダムな勾配ベクトルではなく、前もって用意しておいた複数個の勾配ベクトルをハッシュにより参照させることで計算コストを抑える。
シンプレックスノイズ …… その次元での最小プリミティブ(2次元ノイズの場合、三角形)で補間を行う。次元数に対してオーダーが小さい。コードが難解で読めない。

セルラーノイズ(Cellular Noise)

ランダムに配置した複数の点と計算する点の最小距離を求めます。
見た目の割に実装は非常にシンプルです。
float noise_(vec2 uv) { float o = 1.0; for(int i = 0; i < 64; i++) { vec2 v = random2(vec2(float(i), 0.0)); v += (animation2(random2(v)) - 0.5) * 0.1; o = min(length(v - uv), o); } o = min(pow(o * 5.0, 2.0), 1.0); // 値の勾配を調整 // o = 1.0 - o; // 反転 return o; }
タイル分割した範囲にポイントの取りうる座標を限定して、ポイント間の距離計算コストを一定とする改良版もあります。
トリッキーなテクニックなので少々分かりづらい。
float noise_(vec2 uv) { float o = 1.0; uv *= 8.0; vec2 i_uv = floor(uv); vec2 f_uv = fract(uv); for(int y = -1; y <= 1; y++) { for(int x = -1; x <= 1; x++) { vec2 neightbor = vec2(float(x), float(y)); vec2 r = random2(i_uv + neightbor); r += (animation2(r) - 0.5) * 0.5; r = clamp(r, 0.0, 1.0); o = min(length(f_uv - (neightbor + r)), o); } } o = min(pow(o * 0.75, 2.0), 1.0); // 値の勾配を調整 // o = 1.0 - o; // 反転 return o; }


fBm

周期と振幅の異なるノイズを段階的に重ね合わすことでいい感じのノイズに。
地形生成や雲の生成なんかに。
float fbm = 0.0; float fbm_max = 0.0; for(int i=0; i>=0; i++) { if(u_fbm_iteration == i) break; float f = pow(0.5, float(i)); fbm += f * noise_(v_uv0 * pow(2.0, float(i))); fbm_max += f; } color.xyz *= fbm / fbm_max;


ドメインワーピング(Domain Warping)

iq氏の記事

ノイズのUVを別のノイズの結果で歪ませてしまえ!ってアイデアです。
数行の実装で、非常に強力な効果が得られるのでオススメです。

複数種類のノイズの特性を合成できるので、
例えば、セルラーノイズをバリューノイズで歪めたキメラノイズを生成したりできます。
無限に遊べる。

周期ノイズ(Seamless Noise)

ノイズを周期化することで、リピートが可能になったり、極座標上で継ぎ目なく貼り合わせたりできるようになります。
乱数生成の入力値をうまくmodすることでこれが可能になります。これが割と難しい。

サンプル
サンプル

3Dノイズ

ノイズの生成軸を3次元に拡張してやると3Dノイズがつくれます。
可視化するためには、レイマーチングというやつで多点サンプリングして、いい感じに合成するのがポピュラーなのですが、
何も考えずに実装すると激重なので、あの手この手で高速化します。
濃すぎる内容なので、ここでは割愛。

トップページへ戻る


Tweet Follow @yunta_robo