Bagaimana Anda menghasilkan suara Perlin tileable?

127

Terkait:

Saya ingin menghasilkan suara Perlin tileable. Saya sedang bekerja dari fungsi Paul Bourke PerlinNoise*() , yang seperti ini:

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

Menggunakan kode seperti:

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

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

Memberikan langit seperti

tidak bisa dihilangkan

Yang tidak ubin.

Nilai piksel adalah 0-> 256 (lebar dan tinggi), dan piksel (0,0) menggunakan (x, y) = (0,0) dan piksel (256.256) menggunakan (x, y) = (1,1)

Bagaimana saya bisa membuatnya tileable?

bobobobo
sumber
14
Hanya FYI, yang Anda miliki di sana bukanlah suara Perlin; itu suara fraktal. Perlin noise kemungkinan merupakan fungsi "noise2" yang menghasilkan setiap oktaf dari noise fraktal.
Nathan Reed

Jawaban:

80

Ada dua bagian untuk membuat suara fBm mulus tileable seperti ini. Pertama, Anda perlu membuat fungsi noise Perlin itu sendiri tileable. Berikut adalah beberapa kode Python untuk fungsi noise Perlin sederhana yang bekerja dengan periode hingga 256 (Anda dapat memperpanjangnya sesering mungkin dengan memodifikasi bagian pertama):

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 noise dihasilkan dari penjumlahan "surflets" kecil yang merupakan produk dari gradien berorientasi acak dan fungsi falloff polinomial yang dapat dipisahkan. Ini memberikan wilayah positif (kuning) dan wilayah negatif (biru)

Inti

Surflets memiliki luas 2x2 dan berpusat pada titik kisi integer, sehingga nilai kebisingan Perlin pada setiap titik dalam ruang dihasilkan dengan menjumlahkan surflets di sudut sel yang ditempati.

Penjumlahan

Jika Anda membuat arah gradien membungkus dengan beberapa periode, kebisingan itu sendiri kemudian akan membungkus dengan mulus dengan periode yang sama. Inilah sebabnya mengapa kode di atas mengambil modul koordinat kisi periode sebelum hashing melalui tabel permutasi.

Langkah lain, adalah ketika menjumlahkan oktaf Anda ingin skala periode dengan frekuensi oktaf. Pada dasarnya, Anda ingin setiap oktaf untuk memasang seluruh gambar hanya satu kali, daripada beberapa kali:

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

Gabungkan itu dan Anda mendapatkan sesuatu seperti ini:

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 Noise

Seperti yang Anda lihat, ini memang benar-benar sempurna:

fBm Kebisingan, Ubin

Dengan beberapa penyesuaian kecil dan pemetaan warna, berikut ini adalah gambar cloud ubin 2x2:

Awan!

Semoga ini membantu!

Boojum
sumber
3
im bukan python guy, jadi saya bertanya, bagaimana cara x*2**omengkonversi ke C? itu: x*pow(2,o)atau pow(x*2,o)?
idev
7
x*pow(2, o), karena eksponensial memiliki prioritas lebih tinggi daripada multiplikasi.
John Calsbeek
1
dapatkah seseorang mengonversikan ini ke C? Saya memiliki masalah besar dalam memahami kode ini, karena saya belum pernah melakukan apa pun dengan python. misalnya apa itu anilai? dan saya tidak yakin bagaimana fungsinya dikonversi ke C ... saya mendapatkan garis lurus dalam output saja.
idev
1
Ini jelas merupakan solusi terbaik asalkan Anda baik-baik saja dengan domain kebisingan Anda terikat pada bentuk ubin Anda. Misalnya, ini tidak memungkinkan rotasi sewenang-wenang. Tetapi jika Anda tidak membutuhkan hal seperti itu, ini adalah jawaban yang ideal.
John Calsbeek
1
Catatan: jika Anda ingin menghasilkan ukuran selain 128, JANGAN mengubah nilai numerik pada baris im.putdata(data, 128, 128). (Bagi mereka yang tidak terbiasa dengan python atau PIL: yang mereka maksud adalah skala dan offset, bukan ukuran gambar.)
Antti Kissaniemi
87

Inilah satu cara yang agak cerdik yang menggunakan 4D Perlin noise.

Pada dasarnya, petakan koordinat X piksel Anda ke lingkaran 2D, dan koordinat Y piksel Anda ke lingkaran 2D kedua, dan tempatkan kedua lingkaran tersebut secara orthogonal satu sama lain dalam ruang 4D. Tekstur yang dihasilkan tileable, tidak memiliki distorsi yang jelas, dan tidak mengulangi dengan cara yang tekstur cermin akan lakukan.

Kode copy-paste dari artikel:

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
John Calsbeek
sumber
3
Ini jelas jawaban yang tepat. Tambahkan dimensi adalah trik matematikawan lama. Olinde Rodrigues docet (Sir WR Hamilton docet juga tetapi sedikit kurang)
FxIII
@FxIII, apakah Anda tahu bagaimana Noise4D () ini harus diterapkan? saya ingin mencoba ini tetapi saya tidak tahu bagaimana Noise4D () ini bekerja.
idev
4
Anda dapat menggunakan fungsi noise 4D. Kebisingan simpleks akan menjadi rekomendasi saya. webstaff.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
John Calsbeek
2
terima kasih john! berhasil, manis! tidak ada yang mengatakannya, tetapi: x1, y1, x2, y2 tampaknya semacam penskalaan, jarak yang lebih besar, kebisingan yang terperinci. apakah ini membantu siapa pun.
idev
5
Perhatikan bahwa ini setara secara topologi dengan jawaban bobobobo: pemetaan Anda menanamkan 2-torus ke dalam possible, yang dimungkinkan tanpa distorsi metrik yang pasti Anda dapatkan ketika menanamkannya ke it.
leftaroundabout
22

OK aku mengerti. Jawabannya adalah berjalan di torus dalam derau 3D, menghasilkan tekstur 2D darinya.

torus membungkus 2 dir

Kode:

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

Hasil:

Sekali:

langit miring

Dan ubin:

menunjukkan ubin

bobobobo
sumber
6
Ini agak bekerja, tetapi sepertinya Anda mendapatkan banyak distorsi karena kelengkungan torus.
Nathan Reed
1
Anda dapat benar-benar hanya memodulasi posisi, tapi saya suka semua jawaban yang kreatif / kreatif untuk pertanyaan ini. Begitu banyak cara berbeda untuk melakukan hal yang sama.
saya perhatikan Anda sebenarnya tidak ingin menggunakan nilai 0-1, tetapi 0-0.9999 ... nilai! jadi Anda akan menggunakan: x / width, y / height dll. jika jahitannya tidak cocok (membuat sisi yang berlawanan menghasilkan piksel yang sama). juga sepertinya fungsi PerlinNoise3D () perlu dijepit untuk nilai hasil juga, atau beberapa nilai piksel meluap.
idev
@Nathan, apakah Anda tahu cara memperbaiki distorsi?
idev
2
@ Evo Saya percaya cara untuk memperbaiki distorsi adalah dengan menggunakan metode 4D di jawaban atas pertanyaan ini. ;)
Nathan Reed
16

Salah satu cara sederhana yang dapat saya pikirkan adalah mengambil output dari fungsi noise dan mem-mirror-nya menjadi gambar yang ukurannya dua kali lipat. Sulit untuk dijelaskan, jadi inilah gambarnya: masukkan deskripsi gambar di sini

Sekarang, dalam hal ini, cukup jelas apa yang Anda lakukan ketika Anda melihat ini. Saya dapat memikirkan dua cara untuk (mungkin :-)) menyelesaikan ini:

  1. Anda dapat mengambil gambar yang lebih besar dan kemudian menghasilkan lebih banyak suara di atasnya tetapi (dan saya tidak yakin apakah ini mungkin) fokus ke tengah (sehingga ujungnya tetap sama). Ini bisa menambah sedikit perbedaan yang akan membuat otak Anda berpikir itu bukan hanya gambar cermin.

  2. (Saya juga tidak yakin apakah ini mungkin) Anda bisa mencoba mengutak-atik input ke fungsi noise untuk menghasilkan gambar awal yang berbeda. Anda harus melakukan ini dengan coba-coba, tetapi mencari fitur yang menarik perhatian Anda ketika Anda ubin / mirror dan kemudian mencoba dan membuatnya tidak menghasilkan itu.

Semoga ini membantu.

Richard Marskell - Drackir
sumber
3
Sangat bagus tapi terlalu simetris!
bobobobo
1
@obobobo Itulah yang saya pikirkan langkah-langkah lain akan meringankan. Jadi, Anda dapat menghasilkan "basis" menggunakan metode ini, dan kemudian menambahkan beberapa rincian lebih lanjut atas semuanya untuk membuatnya tampak seperti itu tidak (jadi) dicerminkan.
Richard Marskell - Drackir
Anda mulai mendapatkan beberapa pola aneh ketika Anda melakukan hal semacam ini. Yang satu ini terlihat seperti kupu-kupu. Solusi yang mudah.
notifikasi
Ini adalah pendekatan pertama saya juga, tetapi memiliki masalah, terlihat di sini: dl.dropbox.com/u/6620757/noise_seam.png Saat Anda melewati batas flip Anda menyebabkan penguraian fungsi noise dengan langsung membalikkan kemiringan fungsi. Bahkan jika Anda menerapkan fungsi noise kedua di atas, itu mungkin masih terlihat di output.
Jherico
Ide yang hebat. Ini dapat dengan mudah dilakukan dalam pixel shader menggunakan fungsi gelombang segitiga :tex2d(abs(abs(uv.x)%2.0-1.0), abs(abs(uv.y)%2.0-1.0))
tigrou
10

Versi pertama dari jawaban ini sebenarnya salah, saya telah memperbaruinya

Metode yang saya gunakan dengan sukses adalah membuat ubin domain kebisingan . Dengan kata lain, buat noise2()fungsi basis Anda secara berkala. Jika noise2()periodik dan betabilangan bulat, noise yang dihasilkan akan memiliki periode yang sama dengan noise2().

Bagaimana kita bisa membuat noise2()secara berkala? Dalam sebagian besar implementasi, fungsi ini menggunakan beberapa jenis suara kisi. Artinya, ia mendapat angka acak pada koordinat bilangan bulat, dan menginterpolasinya. Sebagai contoh:

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

Fungsi ini dapat secara sepele dimodifikasi menjadi periodik dengan periode integer. Cukup tambahkan satu baris:

integer_X = integer_X % Period

sebelum menghitung v1dan v2. Dengan cara ini, nilai pada koordinat bilangan bulat akan mengulangi setiap unit Periode, dan interpolasi akan memastikan bahwa fungsi yang dihasilkan lancar.

Perhatikan, bagaimanapun, bahwa ini hanya berfungsi ketika Periode lebih dari 1. Jadi, untuk benar-benar menggunakan ini dalam membuat tekstur mulus, Anda harus mengambil sampel Periode x Periode kuadrat, bukan 1x1.

Lupakan
sumber
Tetapi bagaimana Anda membuat noise2periodik (dengan periode pendek seperti 1 unit)? Saya pikir itulah yang akhirnya ditanyakan. Noise Perlin standar bersifat periodik dengan periode 256 pada setiap sumbu tetapi Anda menginginkan noise yang dimodifikasi dengan periode yang lebih kecil.
Nathan Reed
@Nathan Reed Jika Anda memanggil noise2seperti yang disarankan, Anda akan mendapatkan hasil periodik, apakah fungsinya sendiri periodik atau tidak. Karena argumen membungkus setiap 1 unit.
Nevermind
1
Tapi kemudian Anda mendapatkan jahitan di garis grid, bukan? Karena tidak ada jaminan bahwa noise2 (0, 0.999) berada di dekat noise2 (0, 0), kecuali saya melewatkan sesuatu.
Nathan Reed
1
@Nathan Reed Itu poin bagus. Bahkan, saya baru saja memeriksa ulang kode lama saya dan ternyata saya salah. Saya akan mengedit jawabannya sekarang.
Nevermind
Bagus! Ini sebenarnya jawaban yang bagus sekarang. +1 :)
Nathan Reed
6

Alternatif lain adalah menghasilkan noise menggunakan libnoise libraries. Anda dapat menghasilkan noise melebihi jumlah ruang teoretis tak terbatas, mulus.

Lihatlah yang berikut ini: http://libnoise.sourceforge.net/tutorials/tutorial3.html#tile

Ada juga port XNA di atas di: http://bigblackblock.com/tools/libnoisexna

Jika Anda akhirnya menggunakan port XNA, Anda dapat melakukan sesuatu seperti ini:

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 adalah fungsi untuk memanggil untuk mendapatkan bagian di setiap arah yang akan terhubung dengan mulus dengan sisa tekstur.

Tentu saja, metode ini lebih mahal daripada hanya memiliki tekstur tunggal yang dapat digunakan di berbagai permukaan. Jika Anda ingin membuat beberapa tekstur tileable acak, ini mungkin sesuatu yang menarik bagi Anda.

jallan
sumber
6

Meskipun ada beberapa jawaban di sini yang akan berhasil, kebanyakan dari mereka rumit, lambat dan bermasalah.

Yang perlu Anda lakukan hanyalah menggunakan fungsi penghasil derau berkala . Itu dia!

Implementasi domain publik yang sangat baik berdasarkan algoritma noise "canggih" Perlin dapat ditemukan di sini . Fungsi yang Anda butuhkan adalah pnoise2. Kode ini ditulis oleh Stefan Gustavson, yang telah membuat komentar tajam di sini tentang masalah ini, dan bagaimana orang lain mengambil pendekatan yang salah. Dengarkan Gustavson, dia tahu apa yang dia bicarakan.

Mengenai berbagai proyeksi bola beberapa di sini telah menyarankan: baik, mereka pada dasarnya bekerja (perlahan), tetapi mereka juga menghasilkan tekstur 2D yang merupakan bola pipih, sehingga ujung-ujungnya akan lebih kental, kemungkinan menghasilkan efek yang tidak diinginkan. Tentu saja, jika Anda ingin tekstur 2D Anda diproyeksikan ke bola, itulah cara yang harus dilakukan, tetapi bukan itu yang diminta.

Tal Liron
sumber
4

Berikut cara yang jauh lebih sederhana untuk melakukan noise ubin:

ubin suara perlin dari kode shadertoy

Anda menggunakan pembungkus modular untuk setiap skala kebisingan. Ini sesuai dengan tepi area tidak peduli skala frekuensi apa yang Anda gunakan. Jadi, Anda hanya perlu menggunakan noise 2D normal yang jauh lebih cepat. Berikut adalah kode WebGL langsung yang dapat ditemukan di ShaderToy: https://www.shadertoy.com/view/4dlGW2

Tiga fungsi teratas melakukan semua pekerjaan, dan fBM dilewatkan vektor x / y dalam kisaran 0,0 hingga 1,0.

// 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);
}
Krondike
sumber
1
Tautan gambar Anda terputus. Saya mengambil tebakan terbaik & menggantinya dengan screenshot dari output dari kode shadertoy yang Anda posting. Jika itu tidak benar, harap unggah ulang gambar yang Anda tuju langsung ke server Stack Exchange.
Pikalek
3

Saya memiliki beberapa hasil tidak buruk interpolasi di dekat tepi ubin (tepi-terbungkus), tetapi itu tergantung pada efek apa yang Anda coba capai dan parameter kebisingan yang tepat. Bekerja sangat baik untuk kebisingan yang agak buram, tidak begitu baik dengan yang spikey / berbutir halus.

kaoD
sumber
0

Saya sedang memeriksa utas ini dalam mencari jawaban untuk masalah yang sama, kemudian saya mendapat solusi yang bersih dan kompak dari pengembang kode python ini untuk menghasilkan fraktal noise dari perlin / simplex noise. Kode yang diperbarui disediakan dalam masalah ini (tertutup) dan dapat dilanjutkan dalam pengaturan gradien untuk sisi kanan "generator" sama dengan yang di sisi kiri (dan sama untuk atas dan bawah), seperti di

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

Sepertinya solusi yang elegan dan bersih, saya menghindari menyalin seluruh kode di sini (karena itu bukan solusi saya sendiri), tetapi tersedia di tautan yang diberikan di atas. Semoga ini bisa bermanfaat bagi seseorang yang ingin menghasilkan gambar 2d fraktal ubin seperti yang saya butuhkan, bebas dari artefak atau distorsi.

medan fraktal ubin

Stefano
sumber