ruby perlin noise generator

michael | generative art, code, ruby | Saturday, July 19th, 2008

Been experimenting with Perlin Noise, which is a math function that is used (among other places) in special effects to generate realistic textures. I’m starting to use it to generate color maps instead of picking up colors from photographs. In keeping with my promise to post code samples on this site, here’s a Perlin noise generator in Ruby. The logic came from the pseudocode on this site. (Thanks, Hugo!)

It doesn’t seem to work perfectly… most specifically, it’s supposed to generate a number between -1.0 and 1.0, but it frequently steps over those boundaries. Anyone who can point out why that happens gets a cookie.

It’s working for my purposes, though. Pictures coming soon.


class PerlinMap

  attr_accessor :persistence, :seed, :num_octaves

  def initialize (p = 0.5, s = 100000, o = 6)
    @persistence  = p
    @seed         = s
    @num_octaves  = o
  end

  def result(x, y)
    total = 0
    @num_octaves.times do |i|
      @i = i
      frequency = 2 ** i
      amplitude = @persistence ** i
      total += interpolated_noise(x * frequency, y * frequency) * amplitude
    end
    srand
    total
  end

private 

  def noise(x, y)
    srand (x * y * x + y) + @seed + @i
    rx = rand * 2.0
    rx - 1.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) ) / 16
    sides   = ( noise(x-1, y)   + noise(x+1, y)   + noise(x, y-1)   + noise(x, y+1) ) /  8
    center  =  noise(x, y) / 4
    corners + sides + center
  end

  def interpolate (a, b, x)
   	ft = x * Math::PI
   	f = ( 1.0 - Math.cos( ft ) ) * 0.5
 	  a * ( 1.0 - f ) + b * f
  end

  def interpolated_noise(x, y)
    int_x   = x.to_i
    frac_x  = x - int_x
    int_y   = y.to_i
    frac_y  = y - int_y
    v1 = smooth_noise(int_x,     int_y)
    v2 = smooth_noise(int_x + 1, int_y)
    v3 = smooth_noise(int_x,     int_y + 1)
    v4 = smooth_noise(int_x + 1, int_y + 1)
    i1 = interpolate(v1 , v2 , frac_x)
    i2 = interpolate(v3 , v4 , frac_x)
    interpolate(i1 , i2 , frac_y)
  end

end

Powered by WordPress