Eğilebilir Perlin gürültüsünü nasıl üretirsiniz?


127

İlgili:

Eğilebilir Perlin gürültüsü üretmek istiyorum. Ben den çalışıyorum Paul Bourke en PerlinNoise*() böyledir fonksiyonlar:

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

Gibi kodu kullanarak:

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

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

Gökyüzüne gibi verir

nontileable

Hangi tileable değil.

Piksel değerleri 0-> 256 (genişlik ve yükseklik) ve piksel (0,0), (x, y) = (0,0) ve piksel (256,256) kullanımlarını (x, y) = (1,1) kullanır.

Nasıl tileable hale getirebilirim?


14
Sadece Bilginize, sahip olduğunuz Perlin gürültüsü değil; bu fraktal ses. Perlin gürültüsü muhtemelen fraktal gürültünün her oktavını üreten "noise2" işlevidir.
Nathan Reed

Yanıtlar:


80

Bu şekilde sorunsuz bir şekilde eğilebilir fBm sesi çıkarmanın iki parçası var. Öncelikle, Perlin gürültüsü işlevini kendiliğinden eğilebilir hale getirmeniz gerekir. İşte 256'ya kadar olan herhangi bir periyodla çalışan basit bir Perlin gürültü fonksiyonu için bazı Python kodları (ilk bölümü değiştirerek istediğiniz kadar önemsiz şekilde uzatabilirsiniz):

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))

Perlin gürültüsü, rastgele yönelimli bir gradyanın ve ayrılabilir bir polinom ayrılma fonksiyonunun ürünü olan küçük "surfletlerin" toplamından üretilir. Bu pozitif bölge (sarı) ve negatif bölge (mavi) verir

Çekirdek

Sörfçüler 2x2 boyutundadır ve tam sayı kafes noktalarına odaklanmıştır, bu nedenle uzaydaki her bir noktadaki Perlin gürültüsünün değeri, sörfçüleri işgal ettiği hücrenin köşelerinde toplayarak üretilir.

toplam

Degrade yönlerinin bir süre sarılı kalmasını sağlarsanız, gürültünün kendisi de aynı süreyle sorunsuz şekilde sarılır. Bu yüzden yukarıdaki kod örgü koordinatını permütasyon tablosundan geçirmeden önceki süreyi modulo olarak alıyor.

Diğer adım, oktavları toplarken, dönemi oktavın frekansı ile ölçeklendirmek isteyeceksinizdir. Temel olarak, her oktavın tüm görüntüyü birden çok kez değil, yalnızca bir kez döşemesini istersiniz:

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

Bunu bir araya getirin ve şöyle bir şey olsun:

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")

Tileable fBm Gürültü

Gördüğünüz gibi, bu gerçekten sorunsuz bir şekilde döşeniyor:

fBm Gürültü, Döşenmiş

Bazı küçük ince ayar ve renk eşlemelerinde, işte 2x2 döşenmiş bir bulut resmi:

Bulutlar!

Bu yardımcı olur umarım!


3
bir piton adam değilim, o yüzden soruyorum, nasıl x*2**oC'ye dönüştürür? öyle mi: x*pow(2,o)yoksa pow(x*2,o)?
idev

7
x*pow(2, o)çünkü üstelleşme çarpımdan daha yüksek önceliğe sahip.
John Calsbeek

1
Birisi bunu C'ye dönüştürebilir mi? python ile hiçbir şey yapmadığım için bu kodu anlama konusunda büyük problemlerim var. örneğin, adeğer nedir? ve fonksiyonların C'ye nasıl dönüştüğünden emin değilim, sadece çıktıda düz çizgiler elde ediyorum.
idev

1
Bu kesinlikle en iyi çözümdür, gürültünün etki alanının kiremitinizin şekline bağlı olmasıyla iyi olduğunuz sürece. Örneğin, bu rasgele dönüşlere izin vermez. Ancak böyle bir şeye ihtiyacınız yoksa, bu ideal cevaptır.
John Calsbeek

1
Not: 128'den başka bir boyut oluşturmak istiyorsanız, satırdaki sayısal değerleri DEĞİŞTİRMEYİN im.putdata(data, 128, 128). (Python veya PIL ile aşina olmayanlar için: görüntü boyutu değil, ölçek ve ofset anlamına gelir.)
Antti Kissaniemi 30.03:

87

İşte 4D Perlin gürültüsünü kullanan akıllı bir yol.

Temel olarak, pikselinizin X koordinatını 2B daireye ve pikselinizin Y koordinatını ikinci 2B daireye eşleyin ve bu iki daireyi birbirine 4D uzayda ortogonal yerleştirin. Elde edilen doku eğilebilirdir, belirgin bir bozulma yoktur ve yansıtılmış bir dokunun yapacağı şekilde tekrar etmez.

Makaleden kopyala-yapıştır kodu:

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
Bu kesinlikle doğru cevap. Boyutları eklemek eski bir matematikçi numarasıdır. Olinde Rodrigues docet (Sir WR Hamilton da çok az ama çok az)
FxIII

@ FxIII, bu Noise4D () yönteminin nasıl uygulanması gerektiğini biliyor musunuz? bunu denemek isterim ama bu Noise4D () nin nasıl çalışması gerektiğine dair hiçbir fikrim yok.
idev

4
Herhangi bir 4D gürültü fonksiyonunu kullanabilirsiniz. Simpleks gürültü benim tavsiyem olur. webstaff.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
John Calsbeek

2
teşekkürler John! işe yaradı tatlım! kimse söylemedi, ama: x1, y1, x2, y2 bir çeşit ölçeklendirme, daha büyük mesafe, ayrıntılı ses gibi görünüyor. eğer bu kimseye yardım ederse.
idev

5
Bunun, bobobobo'nun cevabına topolojik olarak eşdeğer olduğunu unutmayın: eşlemeniz, into içine gömdüğünüzde kaçınılmaz olarak elde ettiğiniz metrik çarpıtmalar olmadan mümkün olan 2-torus yerleştirir.
leftaroundabout

22

Tamam anladım. Bunun cevabı, 3B gürültüsündeki bir torusta yürümek ve bunun dışında bir 2B doku oluşturmaktır.

torus 2 dir

Kod:

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

Sonuçlar:

Bir Zamanlar:

tilable sky

Ve kiremitli:

fayans gösteriliyor


6
İşe yarar, ancak torusun eğriliği nedeniyle bir sürü bozulma yaşarsınız.
Nathan Reed

1
pozisyonu değiştirebilirsin ama bu soruya verilen tüm harika / yaratıcı cevapları seviyorum. Aynı şeyi yapmanın pek çok farklı yolu.

aslında 0-1 değer kullanmak istemediğinizi fark ettim, fakat 0-0.9999 ... değerler! bu nedenle şunu kullanırsınız: x / width, y / height vs. aksi takdirde dikişler eşleşmez (zıt kenarları aynı piksel yapar). Ayrıca PerlinNoise3D () işlevinin de sonuç değeri için kenetlenmesi veya bazı piksel değerlerinin taşması gibi görünüyor.
idev

@ Nathan, çarpıklığın nasıl düzeltileceğini biliyor musun?
idev

2
@idev Distorsiyonu düzeltmenin yolunun bu sorunun en üst cevabında 4D yöntemini kullanmak olduğuna inanıyorum. ;)
Nathan Reed

16

Düşünebildiğim basit bir yol, gürültü fonksiyonunun çıktısını almak ve onu iki kat boyutta bir görüntüye yansıtmak / çevirmek olacaktır. Açıklamak zor, işte burada bir görüntü: görüntü tanımını buraya girin

Şimdi, bu durumda, buna baktığınızda ne yaptığınız çok açık. Bunu çözmek için (muhtemelen :-)) iki yol düşünebilirim:

  1. Bu daha büyük görüntüyü çekebilir ve üzerine biraz daha fazla gürültü çıkarabilirsiniz, ancak (ve bunun mümkün olup olmadığından emin değilim) ortasına doğru odaklanır (böylece kenarlar aynı kalır). Beyninizin sadece ayna görüntüleri olmadığını düşünmesine neden olacak ekstra bir fark katabilir.

  2. (Bunun mümkün olup olmadığından da emin değilim) İlk görüntüyü farklı şekilde üretmek için girişleri gürültü işlevine karıştırmayı deneyebilirsiniz. Bunu deneme yanılma yoluyla yapmak zorunda kalacaksınız, ancak kiremit / aynalarken gözlerinizi çizen ve ardından bunları oluşturmamaya çalıştığınız özellikleri incelemek zorundasınız.

Bu yardımcı olur umarım.


3
Çok güzel ama çok simetrik!
bobobobo

1
@bobobobo Diğer adımların azaltacağını düşündüğüm şey buydu. Böylece, bu yöntemi kullanarak bir "temel" oluşturabilir ve daha sonra yansıtılmamış gibi görünmesi için her şey hakkında daha fazla ayrıntı ekleyebilirsiniz.
Richard Marskell - Drakkir

Böyle bir şey yaptığınızda bazı garip desenler elde etmeye başlarsınız. Özellikle bu bir kelebek gibi görünüyor. Yine de kolay çözüm.
not

Bu benim de ilk yaklaşımımdı, ama burada görünen bir sorunu var: dl.dropbox.com/u/6620757/noise_seam.png Bir sınır sınırını geçtiğinizde , gürültü eğiminde hemen eğimi ters çevirerek gürültü işlevinde bir ayrılığa neden oluyorsunuz işlevi. Üzerine ikinci bir gürültü işlevi uygulasanız bile, çıktıda hala görünebilir.
Jherico

İyi fikir. Bu, üçgen dalga fonksiyonunu kullanarak bir piksel gölgelendiricisinde kolayca yapılabilir :tex2d(abs(abs(uv.x)%2.0-1.0), abs(abs(uv.y)%2.0-1.0))
tigrou

10

Bu cevabın ilk sürümü aslında yanlıştı, güncelledim

Başarıyla kullandığım bir yöntem, gürültü alanı döşenir yapmaktır . Başka bir deyişle, temel noise2()işlevinizi periyodik olarak yapın. Eğer noise2()periyodik ve betatam sayıdır, gürültü çıkan aynı dönemine sahip olacaktır noise2().

noise2()Periyodik nasıl yapabiliriz ? Çoğu uygulamada, bu işlev bir çeşit kafes gürültüsü kullanır. Yani, tamsayı koordinatlarında rasgele sayılar alır ve bunları enterpolasyon yapar. Örneğin:

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

Bu fonksiyon tamsayı dönemi ile periyodik hale gelmek için önemsiz bir şekilde değiştirilebilir. Basitçe bir satır ekleyin:

integer_X = integer_X % Period

hesaplamadan önce v1ve v2. Bu şekilde, tamsayı koordinatlarındaki değerler her Dönem birimlerini tekrar eder ve enterpolasyon, sonuçta elde edilen fonksiyonun düzgün olmasını sağlar.

Bununla birlikte, bunun yalnızca Periyot 1'den büyük olduğunda işe yaradığını unutmayın. Dolayısıyla, bunu kesintisiz dokular oluşturmak için kullanmak için, 1x1 değil bir Dönem x Dönem karesi örneklemeniz gerekir.


Fakat nasıl noise2periyodik hale gelirsiniz (1 birim gibi kısa bir süre ile)? Bence sonuçta sorulan soru bu. Standart Perlin gürültüsü, her bir eksende 256 periyodda periyodiktir, ancak daha küçük bir periyodda modifiye edilmiş bir gürültü istiyorsunuz.
Nathan Reed

@Nathan Reed Eğer ararsanız noise2önerildiği şekilde, olacak işlevi kendisi periyodik olup olmadığını periyodik sonuçlar elde ederler. Çünkü argümanlar her 1 birimin etrafına sarılır.
Nevermind

1
Ama sonra şebeke çizgisinde dikiş alırsın, değil mi? Bir şey kaçırmadığım sürece noise2'nin (0, 0.999) noise2 (0, 0) yakınındaki bir şey olduğuna dair hiçbir garanti olmadığı için.
Nathan Reed

1
@ Nathan Reed Bu iyi bir nokta. Aslında eski kodumu yeniden kontrol ettim ve anlaşıldı ki hatalıydım. Şimdi cevabı düzenleyeceğim.
Nevermind

Harika! Bu aslında şimdi iyi bir cevap. +1 :)
Nathan Reed

6

Başka bir alternatif, kütüphanecilik kütüphanelerini kullanarak gürültü üretmektir. Kesintisiz olarak teorik bir sonsuz alan üzerinde gürültü üretebilirsiniz.

Aşağıdakilere bir göz atın: http://libnoise.sourceforge.net/tutorials/tutorial3.html#tile

Ayrıca yukarıdakilerin bir XNA portu vardır: http://bigblackblock.com/tools/libnoisexna

XNA portunu kullanmaya başlarsanız, böyle bir şey yapabilirsiniz:

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, kalan dokulara sorunsuz bir şekilde bağlanacak bölümleri her yöne çekmek için çağrılan işlevdir.

Tabii ki, bu yöntem basitçe birden fazla yüzeyde kullanılabilecek tek bir dokuya sahip olmaktan daha maliyetlidir. Bazı rastgele tileable dokular oluşturmak için arıyorsanız, bu sizi ilgilendiren bir şey olabilir.


6

Burada işe yarayacak bazı cevaplar olmasına rağmen, çoğu karmaşık, yavaş ve sorunlu.

Yapmanız gereken tek şey, periyodik bir ses üretme işlevi kullanmaktır. Bu kadar!

Perlin'in "gelişmiş" gürültü algoritmasına dayanan mükemmel bir kamu malı uygulaması burada bulunabilir . İhtiyacınız olan işlev pnoise2'dir. Kod, burada tam olarak bu konuda ve başkalarının nasıl yanlış bir yaklaşım benimsemiş olduğuna dair bir yorum yapan Stefan Gustavson tarafından yazılmıştır . Gustavson'ı dinle, neden bahsettiğini biliyor.

Çeşitli küresel projeksiyonlara gelince, bazıları burada önerdi: peki, özünde çalışıyorlar (yavaşça), ama aynı zamanda düzensiz bir küre olan 2D bir doku üretiyorlar, böylece kenarlar daha yoğunlaşıyor ve muhtemelen istenmeyen bir etki ortaya çıkmış oluyor. Eğer Tabii ki, eğer niyetinde 2D doku için bir küre üzerine yansıtılan edilecek, o gitmek için yol, ama bunun için isteniyor bu değildi.


4

İşte kiremit gürültüsünü yapmanın çok daha basit bir yolu:

shadertoy kodundan gelen perlin gürültüsü fayans

Gürültünün her ölçeği için modüler bir sargı kullanın. Bunlar hangi frekans ölçeğini kullanırsanız kullanın alanın kenarlarına uyar. Bu nedenle, sadece çok daha hızlı olan normal 2B gürültüyü kullanmanız gerekir. İşte ShaderToy'da bulabileceğiniz canlı WebGL kodu: https://www.shadertoy.com/view/4dlGW2

İlk üç fonksiyon tüm işi yapar ve fBM 0.0 / 1.0 aralığında bir x / y vektöründen geçirilir.

// 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
Resim bağlantınız kesildi. En iyi tahminde bulundum ve gönderdiğiniz shadertoy kodundan çıktının ekran görüntüsüyle değiştirdim. Doğru değilse, lütfen istediğiniz görüntüyü doğrudan Stack Exchange sunucusuna tekrar yükleyin.
Pikalek

3

Döşemenin kenarlarına yakın kenetlenmiş bazı kötü sonuçlar aldım (kenarlara sarılmış), ancak elde etmeye çalıştığınız etkiye ve kesin gürültü parametrelerine bağlıdır. Biraz bulanık gürültü için harika çalışıyor, keskin / ince taneli olanlar için iyi değil.


0

Benzer bir sorunun cevabını ararken bu konuyu kontrol ediyordum, sonra perlin / simpleks gürültüsünden fraktal gürültü üretmek için bu python kodunun geliştiricisinden temiz ve kompakt bir çözüm buldum. Güncellenen kod bu (kapalı) sorunla sağlanmıştır ve "jeneratörün" sağ tarafındaki degradeleri soldakilere eşit (ve üst ve alt için aynı) ayarlamak için devam ettirilebilir.

# 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]

Zarif ve temiz bir çözüm gibi görünüyor, kodun tamamını buraya kopyalamaktan kaçınıyorum (kendi çözümüm olmadığından), ancak yukarıda verilen bağlantıda mevcut. Umarım bu, ihtiyacım olan, eserler veya çarpıtmalardan arındırılmış, eğilebilir bir fraktal 2d görüntüsü üretmek isteyen biri için yararlı olabilir.

eğilebilir fraktal arazi

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.