タイル化可能なパーリンノイズはどのように生成しますか?


127

関連する:

タイル化可能なパーリンノイズを生成したいと思います。私は次のようなポール・バークの PerlinNoise*()機能で働いています:

// alpha is the "division factor" (how much to damp subsequent octaves with (usually 2))
// beta is the factor that multiplies your "jump" into the noise (usually 2)
// n is the number of "octaves" to add in
double PerlinNoise2D(double x,double y,double alpha,double beta,int n)
{
   int i;
   double val,sum = 0;
   double p[2],scale = 1;

   p[0] = x;
   p[1] = y;
   for (i=0;i<n;i++) {
      val = noise2(p);
      sum += val / scale;
      scale *= alpha;
      p[0] *= beta;
      p[1] *= beta;
   }
   return(sum);
}

次のようなコードを使用します。

real val = PerlinNoise2D( x,y, 2, 2, 12 ) ; // test

return val*val*skyColor + 2*val*(1-val)*gray + (1-val)*(1-val)*cloudColor ;

のような空を与える

タイル化不可

これはタイル化できません。

ピクセル値は0-> 256(幅と高さ)で、ピクセル(0,0)は(x、y)=(0,0)を使用し、ピクセル(256,256)は(x、y)=(1,1)を使用します

どうすればタイル化できますか?


14
ご参考までに、あなたが持っているのはパーリンノイズではありません。フラクタルノイズです。パーリンノイズは、フラクタルノイズの各オクターブを生成する「noise2」関数である可能性があります。
ネイサンリード

回答:


80

このようにシームレスにタイル化可能なfBmノイズを作成するには2つの部分があります。まず、Perlinノイズ関数自体をタイル化可能にする必要があります。最大256までの任意の期間で動作する単純なPerlinノイズ関数のPythonコードを次に示します(最初のセクションを変更することで、好きなだけ簡単に拡張できます)。

import random
import math
from PIL import Image

perm = range(256)
random.shuffle(perm)
perm += perm
dirs = [(math.cos(a * 2.0 * math.pi / 256),
         math.sin(a * 2.0 * math.pi / 256))
         for a in range(256)]

def noise(x, y, per):
    def surflet(gridX, gridY):
        distX, distY = abs(x-gridX), abs(y-gridY)
        polyX = 1 - 6*distX**5 + 15*distX**4 - 10*distX**3
        polyY = 1 - 6*distY**5 + 15*distY**4 - 10*distY**3
        hashed = perm[perm[int(gridX)%per] + int(gridY)%per]
        grad = (x-gridX)*dirs[hashed][0] + (y-gridY)*dirs[hashed][1]
        return polyX * polyY * grad
    intX, intY = int(x), int(y)
    return (surflet(intX+0, intY+0) + surflet(intX+1, intY+0) +
            surflet(intX+0, intY+1) + surflet(intX+1, intY+1))

パーリンノイズは、ランダムに方向付けられた勾配と分離可能な多項式フォールオフ関数の積である小さな「サーフレット」の合計から生成されます。これにより、正の領域(黄色)と負の領域(青)が得られます。

カーネル

サーフレットは2x2の範囲を持ち、整数の格子点を中心としているため、空間内の各点でのパーリンノイズの値は、占有するセルのコーナーでサーフレットを合計することによって生成されます。

総和

グラデーションの方向をある周期でラップすると、ノイズ自体も同じ周期でシームレスにラップします。これが、上記のコードが順列テーブルをハッシュする前に周期を法とする格子座標を取る理由です。

もう1つのステップは、オクターブを合計するときに、オクターブの周波数で周期をスケーリングすることです。基本的に、複数の回ではなく、各オクターブで画像全体を1回タイル表示する必要があります。

def fBm(x, y, per, octs):
    val = 0
    for o in range(octs):
        val += 0.5**o * noise(x*2**o, y*2**o, per*2**o)
    return val

それをまとめると、次のようになります。

size, freq, octs, data = 128, 1/32.0, 5, []
for y in range(size):
    for x in range(size):
        data.append(fBm(x*freq, y*freq, int(size*freq), octs))
im = Image.new("L", (size, size))
im.putdata(data, 128, 128)
im.save("noise.png")

タイル化可能なfBmノイズ

ご覧のとおり、これは実際にシームレスにタイル表示されます。

fBmノイズ、タイル

いくつかの小さな調整とカラーマッピングを使用して、2x2のタイルのクラウドイメージを次に示します。

雲!

お役に立てれば!


3
私はPythonの男ではないので、私は尋ねます、どのようx*2**oにCに変換しますか?それですか:x*pow(2,o)またはpow(x*2,o)
idev

7
x*pow(2, o)、べき乗は乗算よりも優先順位が高いため。
ジョンカルスビーク

1
誰かがこれをCに変換できますか?私はPythonで何もしたことがないので、このコードを理解するのに大きな問題があります。たとえば、a価値とは何ですか?そして、関数がどのようにCに変換されるかわからない...私は出力のみで直線を得る。
idev

1
これは間違いなく、ノイズの領域がタイルの形状に関係している限り、最良のソリューションです。たとえば、これは任意の回転を許可しません。しかし、あなたがそのようなものを必要としないなら、これは理想的な答えです。
ジョンカルスビーク

1
注:128以外のサイズを生成する場合は、行の数値を変更しないでくださいim.putdata(data, 128, 128)。(PythonまたはPILに不慣れな人のために:それらは、画像サイズではなく、スケールとオフセットを意味します。)
アンティキサニエミ

87

4D Perlinノイズを使用する、かなり賢い方法を次に示します。

基本的に、ピクセルのX座標を2D円にマッピングし、ピクセルのY座標を2番目の2D円にマッピングし、2つの円を4D空間で互いに直交させます。結果のテクスチャはタイル化可能で、明らかな歪みはなく、ミラーテクスチャのように繰り返されません。

記事からコードをコピーして貼り付ける:

for x=0,bufferwidth-1,1 do
    for y=0,bufferheight-1,1 do
        local s=x/bufferwidth
        local t=y/bufferheight
        local dx=x2-x1
        local dy=y2-y1

        local nx=x1+cos(s*2*pi)*dx/(2*pi)
        local ny=y1+cos(t*2*pi)*dy/(2*pi)
        local nz=x1+sin(s*2*pi)*dx/(2*pi)
        local nw=y1+sin(t*2*pi)*dy/(2*pi)

        buffer:set(x,y,Noise4D(nx,ny,nz,nw))
    end
end

3
これは間違いなく正しい答えです。次元を追加することは、古い数学者のトリックです。オリンデロドリゲスドセ(WRハミルトンドセirも少し少ない)
FxIII

@FxIII、このNoise4D()の実装方法を知っていますか?私はこれを試してみたいと思いますが、このNoise4D()がどのように機能するかについてはわかりません。
idev

4
任意の4Dノイズ関数を使用できます。シンプレックスノイズが推奨されます。webstaff.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
ジョンカルスベーク

2
ありがとうジョン!うまくいきました!誰もそれを言っていませんが、x1、y1、x2、y2は何らかのスケーリング、より大きな距離、詳細なノイズのようです。これが誰かを助けるなら。
idev

5
これはboboboboの答えとトポロジ的に同等であることに注意してください。マッピングは2トーラスをℝ⁴に埋め込みます。
左辺約

22

はい、分かりました。答えは、3Dノイズでトーラスを歩いて、2Dテクスチャを生成することです。

トーラスは2日を包みます

コード:

Color Sky( double x, double y, double z )
{
  // Calling PerlinNoise3( x,y,z ),
  // x, y, z _Must be_ between 0 and 1
  // for this to tile correctly
  double c=4, a=1; // torus parameters (controlling size)
  double xt = (c+a*cos(2*PI*y))*cos(2*PI*x);
  double yt = (c+a*cos(2*PI*y))*sin(2*PI*x);
  double zt = a*sin(2*PI*y);
  double val = PerlinNoise3D( xt,yt,zt, 1.5, 2, 12 ) ; // torus

  return val*val*cloudWhite + 2*val*(1-val)*gray + (1-val)*(1-val)*skyBlue ;
}

結果:

一度:

ティラブルスカイ

そしてタイル張り:

タイルを見せて


6
少しは機能しますが、トーラスの曲率による歪みがたくさんあるように見えます。
ネイサンリード

1
あなたは本当に位置をモジュロすることができますが、私はこの質問に対するすべての素晴らしい/創造的な答えが大好きです。同じことをするための多くの異なる方法。

私はあなたが実際に0-1の値を使用したくないことに気づきましたが、0-0.9999 ...の値!したがって、x / width、y / heightなどを使用します。そうしないと、縫い目が一致しません(反対側のエッジが正確に同じピクセルになります)。また、PerlinNoise3D()関数も結果値をクランプする必要があるか、ピクセル値がオーバーフローするようです。
idev

@ネイサン、歪みを修正する方法を知っていますか?
idev

2
@idev歪みを修正する方法は、この質問の一番上の回答で4Dメソッドを使用することだと思います。;)
ネイサンリード

16

私が考えることができる1つの簡単な方法は、ノイズ関数の出力を取得し、それを2倍のサイズの画像にミラーリング/フリップすることです。説明するのは難しいので、ここに画像があります: ここに画像の説明を入力してください

さて、この場合、これを見たときに何をしたかは明らかです。私は2つの方法を考えて(おそらく:-)これを解決します:

  1. その大きな画像を撮影し、その上にさらにノイズを生成することもできますが(これが可能かどうかはわかりません)、中央に焦点を合わせます(したがって、エッジは同じままです)。それはあなたの脳に、それが単なる鏡像ではないと思わせるような違いを少し加えるかもしれません。

  2. (これが可能かどうかもわかりません)ノイズ関数への入力をいじって、初期イメージを異なる方法で生成することができます。試行錯誤でこれを行う必要がありますが、タイル化/ミラーリングするときに目を引く機能を探し、それらを生成しないようにしてください。

お役に立てれば。


3
とてもいいけど対称的すぎる!
ボボボボ

1
@boboboboそれは私が他のステップが軽減するだろうと思っていたものです。そのため、このメソッドを使用して「ベース」を生成し、全体にさらに詳細を追加して、ミラーリングされていないように見せることができます。
リチャードマースケル-ドラッカー

この種のことをすると、奇妙なパターンを取得し始めます。これは特に蝶のように見えます。しかし、簡単な解決策。
-notlesh

これは、あまりにも私の最初のアプローチでしたが、それは問題を抱えている、目に見えるここに: dl.dropbox.com/u/6620757/noise_seam.png あなたはフリップ境界を越えるとして、あなたは即座の傾きを反転させてノイズ関数でばらばらの原因となります関数。上部に2番目のノイズ関数を適用しても、出力に表示される場合があります。
ジェリコ

いい案。これは簡単に使用して、ピクセルシェーダで行うことができる三角波関数:tex2d(abs(abs(uv.x)%2.0-1.0), abs(abs(uv.y)%2.0-1.0))
tigrou

10

この答えの最初のバージョンは実際には間違っていた、私はそれを更新しました

私がうまく使った方法は、ノイズ領域をタイル状にすることです。つまり、基本noise2()機能を定期的にします。場合noise2()周期的であり、beta整数であり、得られたノイズは同じ周期を有することになりますnoise2()

どうすればnoise2()定期的にできますか?ほとんどの実装では、この関数はある種の格子ノイズを使用します。つまり、整数座標で乱数を取得し、それらを補間します。例えば:

function InterpolatedNoise_1D(float x)

  integer_X    = int(x)
  fractional_X = x - integer_X

  v1 = SmoothedNoise1(integer_X)
  v2 = SmoothedNoise1(integer_X + 1)

  return Interpolate(v1 , v2 , fractional_X)

end function

この関数は、整数周期で周期的になるように簡単に変更できます。1行追加するだけです:

integer_X = integer_X % Period

計算する前v1v2。このように、整数座標の値は周期単位ごとに繰り返され、補間により結果の関数がスムーズになります。

ただし、これは、Periodが1を超える場合にのみ機能することに注意してください。したがって、これをシームレステクスチャの作成に実際に使用するには、1x1ではなく、Period x Period正方形をサンプリングする必要があります。


しかし、どのようにnoise2定期的に(1単位などの短い期間で)作成しますか?それが最終的に質問が求めていることだと思います。標準のパーリンノイズは、各軸で256の周期で周期的ですが、より短い周期で修正されたノイズが必要です。
ネイサンリード

@Nathan Reed noise2提案どおりに呼び出すと、関数自体が周期的であるかどうかにかかわらず、周期的な結果得られます。引数は 1ユニットごとに折り返すためです。
ネヴァーマインド

1
しかし、グリッド線に縫い目ができますよね?何も見逃していない限り、noise2(0、0.999)がnoise2(0、0)に近いことを保証するものではありません。
ネイサンリード

1
@ネイサンリードそれは良い点です。実際、古いコードを再確認したところ、間違っていたことがわかりました。今すぐ答えを編集します。
ネヴァーマインド

すばらしいです!これは今では実際に良い答えです。+1 :)
ネイサンリード

6

もう1つの方法は、libnoiseライブラリを使用してノイズを生成することです。理論的に無限の空間でシームレスにノイズを生成できます。

以下をご覧くださいhttp : //libnoise.sourceforge.net/tutorials/tutorial3.html#tile

上記のXNAポートもあります:http : //bigblackblock.com/tools/libnoisexna

XNAポートを使用することになった場合、次のようなことができます。

Perlin perlin = new Perlin();
perlin.Frequency = 0.5f;                //height
perlin.Lacunarity = 2f;                 //frequency increase between octaves
perlin.OctaveCount = 5;                 //Number of passes
perlin.Persistence = 0.45f;             //
perlin.Quality = QualityMode.High;
perlin.Seed = 8;

//Create our 2d map
Noise2D _map = new Noise2D(CHUNKSIZE_WIDTH, CHUNKSIZE_HEIGHT, perlin);

//Get a section
_map.GeneratePlanar(left, right, top, down);

GeneratePlanarは、残りのテクスチャとシームレスに接続する各方向のセクションを取得するために呼び出す関数です。

もちろん、この方法は、複数のサーフェスで使用できる単一のテクスチャを用意するよりもコストがかかります。ランダムなタイル化可能なテクスチャを作成しようとしている場合、これは興味のあるものかもしれません。


6

ここにはいくつかの答えがありますが、それらのほとんどは複雑で遅く、問題があります。

本当に必要なのは、周期的なノイズ生成機能を使用することだけです。それでおしまい!

Perlinの「高度な」ノイズアルゴリズムに基づく優れたパブリックドメインの実装は、ここにあります。必要な関数はpnoise2です。コードはStefan Gustavsonによって作成されました。StefanGustavsonは、この問題について正確にコメントし、他の人がどのように間違ったアプローチを取っているかについてコメントしました。グスタフソンを聞いて、彼は彼が何について話しているか知っています。

さまざまな球面投影に関して、いくつかの提案があります:まあ、本質的には(ゆっくりと)動作しますが、平らな球体である2Dテクスチャも生成するため、エッジがより凝縮され、望ましくない効果が生じる可能性があります。あなたがあればもちろん、予定の球体に投影するためにあなたの2Dテクスチャのために、それが移動するための方法ですが、それはのために頼まれていたものではありません。


4

タイルノイズを行うはるかに簡単な方法を次に示します。

シェードロイコードからのパーリンノイズのタイリング

ノイズのスケールごとにモジュラーラップアラウンドを使用します。これらは、使用する周波数スケールに関係なく、領域の端に適合します。そのため、通常の2Dノイズを使用するだけで、はるかに高速になります。ShaderToyにあるライブWebGLコードは次のとおりです。https ://www.shadertoy.com/view/4dlGW2

上位3つの関数はすべての作業を行い、fBMには0.0〜1.0の範囲のベクトルx / yが渡されます。

// Tileable noise, for creating useful textures. By David Hoskins, Sept. 2013.
// It can be extrapolated to other types of randomised texture.

#define SHOW_TILING
#define TILES 2.0

//----------------------------------------------------------------------------------------
float Hash(in vec2 p, in float scale)
{
    // This is tiling part, adjusts with the scale...
    p = mod(p, scale);
    return fract(sin(dot(p, vec2(35.6898, 24.3563))) * 353753.373453);
}

//----------------------------------------------------------------------------------------
float Noise(in vec2 x, in float scale )
{
    x *= scale;

    vec2 p = floor(x);
    vec2 f = fract(x);
    f = f*f*(3.0-2.0*f);
    //f = (1.0-cos(f*3.1415927)) * .5;
    float res = mix(mix(Hash(p,                  scale),
        Hash(p + vec2(1.0, 0.0), scale), f.x),
        mix(Hash(p + vec2(0.0, 1.0), scale),
        Hash(p + vec2(1.0, 1.0), scale), f.x), f.y);
    return res;
}

//----------------------------------------------------------------------------------------
float fBm(in vec2 p)
{
    float f = 0.4;
    // Change starting scale to any integer value...
    float scale = 14.0;
    float amp = 0.55;
    for (int i = 0; i < 8; i++)
    {
        f += Noise(p, scale) * amp;
        amp *= -.65;
        // Scale must be multiplied by an integer value...
        scale *= 2.0;
    }
    return f;
}

//----------------------------------------------------------------------------------------
void main(void)
{
    vec2 uv = gl_FragCoord.xy / iResolution.xy;

#ifdef SHOW_TILING
    uv *= TILES;
#endif

    // Do the noise cloud (fractal Brownian motion)
    float bri = fBm(uv);

    bri = min(bri * bri, 1.0); // ...cranked up the contrast for no reason.
    vec3 col = vec3(bri);

#ifdef SHOW_TILING
    vec2 pixel = (TILES / iResolution.xy);
    // Flash borders...
    if (uv.x > pixel.x && uv.y > pixel.y                                        // Not first pixel
    && (fract(uv.x) < pixel.x || fract(uv.y) < pixel.y) // Is it on a border?
    && mod(iGlobalTime-2.0, 4.0) < 2.0)                 // Flash every 2 seconds
    {
        col = vec3(1.0, 1.0, 0.0);
    }
#endif
    gl_FragColor = vec4(col,1.0);
}

1
画像リンクが機能しなくなりました。最善の推測を行い、投稿したシェードトイコードの出力のスクリーンショットに置き換えました。それが正しくない場合は、目的のイメージをStack Exchangeサーバーに直接再アップロードしてください。
ピカレック

3

タイルのエッジ付近で補間を行うと、悪くない結果がいくつかあります(エッジラップ)が、達成しようとしている効果と正確なノイズパラメーターによって異なります。ややぼやけたノイズに最適ですが、スパイク/細かいノイズにはあまり適していません。


0

私はこのスレッドを同様の問題の答えを探してチェックしていましたが、perlin / simplexノイズからフラクタルノイズを生成するためにこのPythonコードの開発者からクリーンでコンパクトなソリューションを得ました。更新されたコードは、この(クローズド)問題で提供されており、「ジェネレーター」の右側の勾配を左側の勾配に等しく設定することで再開できます(上と下で同じ)。

# Gradients
angles = 2*np.pi*np.random.rand(res[0]+1, res[1]+1)
gradients = np.dstack((np.cos(angles), np.sin(angles)))
# Make the noise tileable
gradients[-1,:] = gradients[0,:]
gradients[:,-1] = gradients[:,0]

エレガントでクリーンなソリューションのように見えますが、ここではコード全体をコピーすることは避けています(自分のソリューションではないため)が、上記のリンクから入手できます。これが、私が必要とするこのようなタイル化可能なフラクタル2d画像を、アーティファクトや歪みのない状態で作成したい人に役立つことを願っています。

タイル化可能なフラクタル地形

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.