ref: wikipedia:パーリンノイズ
ref: Perlin Noise
CG 業界とかで有名な、なんか雲みたいなノイズのことです。参考サイトの擬似コードをそのまま Ruby に移植してみました。
# http://freespace.virgin.net/hugo.elias/models/m_perlin.htm class PerlinNoise def initialize(seed) @seed = seed end def noise(x, y) ([@seed, x, y].hash & 65535) / 65536.0 end def smooth_noise(x, y) corners = noise(x-1, y-1) + noise(x-1, y+1) + noise(x+1, y-1) + noise(x+1, y+1) sides = noise(x , y-1) + noise(x , y+1) + noise(x-1, y ) + noise(x+1, y ) center = noise(x , y ) center / 4 + sides / 8 + corners / 16 end def linear_interpolate(a, b, x) a * (1 - x) + b * x end def cosine_interpolate(a, b, x) f = (1 - Math.cos(x * Math::PI)) / 2 a * (1 - f) + b * f end #alias interpolate linear_interpolate alias interpolate cosine_interpolate def interpolate_noise(x, y) interpolate( interpolate( smooth_noise(x.floor , y.floor ), smooth_noise(x.floor+1, y.floor ), x - x.floor), interpolate( smooth_noise(x.floor , y.floor+1), smooth_noise(x.floor+1, y.floor+1), x - x.floor), y - y.floor) end def perlin_noise(x, y) (0...$number_of_octaves).map do |i| frequency = 2.0 ** i amplitude = $persistence ** i interpolate_noise(x * frequency, y * frequency) * amplitude end.inject(&:+) end end # generate png require "zlib" width = height = 300 depth, color_type = 8, 2 $number_of_octaves = 4 $persistence = 1.0 / 4 pn = PerlinNoise.new(1) img_data = (0...height).map do |y| (0...width).map do |x| c = (pn.perlin_noise(x / 30.0, y / 30.0) * 255).floor & 255 [c, c, c] end end def chunk(type, data) [data.bytesize, type, data, Zlib.crc32(type + data)].pack("NA4A*N") end print "\x89PNG\r\n\x1a\n" print chunk("IHDR", [width, height, 8, 2, 0, 0, 0].pack("NNCCCCC")) raw_data = img_data.map {|line| ([0] + line.flatten).pack("C*") }.join print chunk("IDAT", Zlib::Deflate.deflate(raw_data)) print chunk("IEND", "")
座標 (x, y) に 0 から 1 の乱数値を割り当てる必要があるのですが、でっかい配列やハッシュを確保するのはダサいと思ったので、[@seed, x, y].hash を乱数として使ってみました。最近の Ruby 1.9 (trunk) は、Fixnum#hash でもぱっと見は乱数として使えるくらいにわけわからない数字が帰ってくるようになっています (そして起動するたびに変わる) 。
$ ruby19 -e 'p 1.hash' 1345983667 $ ruby19 -e 'p 1.hash' -2093709963 $ ruby19 -e 'p 1.hash' 1668111793
実行の様子。
$ time ruby19 perlin.rb > perlin.png real 1m7.940s user 1m7.936s sys 0m0.008s
おそっ……。Ruby おそっ……。1.9.f ならどのくらい速いのかな (試してない) 。
RGB ごとに別のパーリンノイズ割り当ててパラメータいじったらなんかサイケデリックなの出来た。