JoshDreamland icon

Noise World

JoshDreamland | PRO | 11/13/13 09:04:18 PM UTC | 0 ⭐ | 260 👁️ | Never ⏰ | []
Java |

8.56 KB

|

None

|

0 👍

/

0 👎

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// at your option) any later version.
 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
import java.util.Random;  // I can't stand arbitrary output
import java.util.TreeMap; // FloorEntry/CeilEntry is needed for sampling
 
// Image parameters: This is the stuff that's easy to change
final int W = 2400, H = 1350;
final int w = W, h = H;
final long seed  = 0x09f911029D74E35BL;
final long seed2 = 0xD84156C5635688C0L;
final long seed3 = 0xDECAFF;
final int noiseCount = 512;
final int cloudFreq = 16;
final float baser = 32, basey = 96;
final float dist1 = .002, dist2 = .0002;
final int nth = 0;
 
// Other locals
PImage img = createImage(w, h, RGB);
 
// Set up our noise and run it.
void setup() { 
  size(W, H);
  img.loadPixels();
  
  Combiner earth = new Combiner((new Octave())
    .addOctave(1.0,   new Worley(seed, noiseCount, nth, dist1))
    .addOctave(1./12, new Worley(seed + 1, noiseCount * 4, nth, dist2))
  ).addLevel(0, new ColorSampler(0xFF0000FF))
   .addLevel(.6, new ColorSampler(0xFF00FFFF))
   .addLevel(.8, new ColorSampler(0xFFFFFEB4))
   .addLevel(1.4, new Combiner((new Octave())
      .addOctave(1./1,  new Perlin(seed2, noiseCount / 4).square().square().square())
      .addOctave(1./2,  new Perlin(seed2, noiseCount / 2))
      .addOctave(1./4,  new Perlin(seed2, noiseCount * 1))
      .addOctave(1./8,  new Perlin(seed2, noiseCount * 2))
      .addOctave(1./16, new Perlin(seed2, noiseCount * 4))
    ).addLevel(0,  new ColorSampler(0xFF006000))
     .addLevel(.5, new ColorSampler(0xFF008000))
     .addLevel(1,  new ColorSampler(0xFF00C000))
  );
  
  Combiner clouds = new Combiner((new Octave())
    .addOctave(2.0,  new Perlin(seed3, cloudFreq * 1).square())
    .addOctave(1./2, new Perlin(seed3, cloudFreq * 2).square())
    .addOctave(1./4, new Perlin(seed3, cloudFreq * 4))
    .addOctave(1./8, new Perlin(seed3, cloudFreq * 8))
  ).addLevel(1,   earth)
   .addLevel(2, new ColorSampler(0xFFFFFFFF))
   .addLevel(3, new ColorSampler(0xFFC0C0C0));
  
  Sampler pic = clouds;
  
  int ind = 0;
  for (int y = 0; y < h; ++y) {
    float yf = y / (float)h;
    for (int x = 0; x < w; ++x)
      img.pixels[ind++] = pic.get(x / (float)w, yf); 
  }
  img.updatePixels();
  image(img, 0, 0, W, H);
  save("cloudy-world.png");
}
 
 
// Utility functions, boilerplate boilerplate boilerplate
static double sqr(double x) { return x*x; }
static void arrins(int[] arr, int x, int ind) {
  for (int i = arr.length - 1; i > ind; --i)
    arr[i] = arr[i-1];
  arr[ind] = x;
} // This function exists because Java generics don't like primitives.
// This will work in Java 7. OHWAITNOITWONT
static void arrins(double[] arr, double x, int ind) {
  for (int i = arr.length - 1; i > ind; --i)
    arr[i] = arr[i-1];
  arr[ind] = x;
}
float clamp(float v, float min, float max) {
  return v < min? min : v > max? max : v;
}
float sigmoid(float a) {
  return 1 / (1 + exp(-4*a+2));
}
float slerp(float x, float y, float a) {
  return lerp(x, y, sin(a * PI/2));
}
 
color mergeColor2(color c1, color c2, float a) {
  int r1 = (c1 & 0xFF0000) >> 16, g1 = (c1 & 0xFF00) >> 8, b1 = (c1 & 0xFF);
  int r2 = (c2 & 0xFF0000) >> 16, g2 = (c2 & 0xFF00) >> 8, b2 = (c2 & 0xFF);
  if (a <= .5) {
    color m = color(max(r1, (r1 + r2)/2), max(g1, (g1 + g2)/2), max(b1, (b1 + b2)/2));
    return lerpColor(c1, m, 2 * a);
  }
  color m = color(max(r2, (r1 + r2)/2), max(g2, (g1 + g2)/2), max(b2, (b1 + b2)/2));
  return lerpColor(m, c2, a * 2 - 1);
}
 
color mergeColor(color c1, color c2, float a) {
  int r1 = (c1 & 0xFF0000) >> 16, g1 = (c1 & 0xFF00) >> 8, b1 = (c1 & 0xFF);
  int r2 = (c2 & 0xFF0000) >> 16, g2 = (c2 & 0xFF00) >> 8, b2 = (c2 & 0xFF);
  color m = color(max(r1, r2), max(g1, g2), max(b1, b2));
  return (a < .5)? lerpColor(c1, m, 2 * a) : lerpColor(m, c2, a * 2 - 1);
}
 
color slerpColor(color c1, color c2, float a) {
  return mergeColor(c1, c2, sigmoid(a));
}
 
// Interfaces, classes
 
static interface Noise {
  public float get(float x, float y);
}
 
 
// This is where the actual logic starts.
 
// This is a class to sample one octave of worley noise.
static class Worley implements Noise {
  // These are the variables needed by the actual image logic
  double[][] points;
  int k;
  double max;
  
  // This creates our points, scattering them randomly according to the seed.
  public Worley(long seed, int npts, int k, double max) {
    Random worlrand = new Random(seed);
    points = new double[npts][2];
    this.k = k;
    this.max = max;
    
    for (int i = 0; i < points.length; ++i) {
      points[i][0] = worlrand.nextDouble();
      points[i][1] = worlrand.nextDouble();
    }
  }
  
  // This samples a point by computing the squared distance to the nearest k points,
  // then returning the kth closest 
  public float get(float x, float y) {
    double[] d2 = new double[k + 1];
    int[] ptnum = new int[k + 1];
    for (int i = 0; i <= k; ++i) {
      d2[i] = Double.POSITIVE_INFINITY;
      ptnum[i] = 0;
    }
    for (int i = 0; i < points.length; ++i) {
      double d2i = sqr(x - points[i][0]) + sqr(y - points[i][1]);
      for (int j = 0; j <= k; ++j)
        if (d2i < d2[j]) {
          arrins(d2, d2i, j);
          arrins(ptnum, i, j);
          break;
        }
    }
    return (float)(d2[k] / max);
  }
}
 
class Perlin implements Noise {
  float[][] grid;
  int size;
  
  Perlin(long seed, int dim) {
    Random r = new Random(seed);
    grid = new float[dim][dim];
    size = dim;
    for (int i = 0; i < dim; ++i)
      for (int j = 0; j < dim; ++j)
        grid[i][j] = (float)r.nextDouble();
  }
  
  Perlin square() {
    for (int i = 0; i < size; ++i)
      for (int j = 0; j < size; ++j)
        grid[i][j] *= grid[i][j];
    return this;
  }
  
  float get(float x, float y) {
    float i = clamp(y, 0, 1) * (size - 1);
    float j = clamp(x, 0, 1) * (size - 1);
    int in = (int)floor(i), ix = (int)ceil(i);
    int jn = (int)floor(j), jx = (int)ceil(j);
    return slerp(
      slerp(grid[in][jn], grid[in][jx], (j - jn)),
      slerp(grid[ix][jn], grid[ix][jx], (j - jn)),
      (i - in)
    );
  }
}
 
class Octave implements Noise {
  ArrayList<Noise>  octaves = new ArrayList<Noise>();
  ArrayList<Double> factors = new ArrayList<Double>();
  public Octave addOctave(double fac, Noise n) {
    octaves.add(n);
    factors.add(fac);
    return this;
  }
  float get(float x, float y) {
    double accum = 0;
    for (int i = 0; i < octaves.size(); ++i)
      accum += factors.get(i) * octaves.get(i).get(x, y);
     return (float)accum;
  }
}
 
interface Interpolator {
  color erp(color x, color y, float a);
}
 
class LinearInterpolator implements Interpolator {
  color erp(color x, color y, float a) {
    return lerpColor(x, y, a);
  }
}
 
interface Sampler {
  color get(float x, float y);
}
 
class ColorSampler implements Sampler {
  color c;
  public ColorSampler(color c) { this.c = c; }
  public color get(float x, float y) { return c; }
}
 
class Combiner implements Sampler {
  private class SampLerp {
    public Sampler s;
    public Interpolator i;
    public SampLerp(Sampler ss, Interpolator si) { s = ss; i = si; }
  }
  TreeMap<Float, SampLerp> subsamplers = new TreeMap<Float, SampLerp>();
  Noise sampleNoise;
  
  public Combiner(Noise n) { sampleNoise = n; }
  
  public Combiner addLevel(float threshold, Sampler s) { return addLevel(threshold, s, new LinearInterpolator()); }
  public Combiner addLevel(float threshold, Sampler s, Interpolator i) { subsamplers.put(threshold, new SampLerp(s, i)); return this; }
  
  public color get(float x, float y) {
    float noise = sampleNoise.get(x, y);
    Float less = subsamplers.floorKey(noise);
    Float more = subsamplers.ceilingKey(noise);
    if (less == null) less = more;
    if (less == more || more == null)
      return subsamplers.get(less).s.get(x, y);
    SampLerp sl = subsamplers.get(less);
    return sl.i.erp(sl.s.get(x, y), subsamplers.get(more).s.get(x, y), (noise - less)/(more - less));
  }
}

Comments

  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎