Noise Blending with the AU Library

New noise fieldNow that my AU Library is out (in beta, anyway) I thought I’d show how to write the noise blending code from an earlier post, but this time using the AU Library. Although I do a little more work in setup(), the draw() is much simpler, and the results are much nicer.

Here’s what’s changed:

  • I replaced the 3D array of floats with a single AUMultiField object.
  • I replaced the first two lines of draw() (which were pretty messy) with simple calls to an AUStepper object.
  • I replaced the call to curve() with a call to a per-pixel AUCurve object. This means we move through the curves at a smooth, constant pace.
  • These changes mean that the noise fields and a couple of other variables no longer need to be global. The only globals are the AUStepper and the AUCurve objects.

I could scrunch the code down even more by doing away with the AUMultiField that holds the noise. After all, there’s really no reason to compute it and save it since it’s only used once. The expressions to get the noise could be rolled right into each AUCurve constructor. But I think this is a little easier to follow, and it demonstrates how to use AUMultiField to hold this kind of data, so I left it this way.

import AULibrary.*;

// A little sketch to demonstrate the multiple noise field interpolation technique.
// No guarantee or warranty of any kind. You may use and distribute this freely.
// Andrew Glassner, September 16, 2014

AUStepper Stepper;  // tracks our progress through the fields
AUCurve[][] NoiseCurves;  // one curve of noise per pixel

void setup() {
  size(500, 500);
  
  AUMultiField noiseArrays;       // list of noise fields
  int numFields = 4;              // how many fields you want to use. Must be > 0
  int cycleLength = 25;           // number of frames to get from one field to another

  Stepper = new AUStepper(numFields, cycleLength);    // create the stepper 
  Stepper.setAllEases(AULibrary.EASE_LINEAR);
  
  noiseArrays = new AUMultiField(this, numFields, width, height);
  for (int n=0; n<numFields; n++) { // for each field n,
    for (int y=0; y<height; y++) {  // look at all the pixels
      for (int x=0; x<width; x++) {
        float noiseX = (x*.02) + (n*width);  // .02 was chosen by eye; it's
        float noiseY = (y*.02) + (n*height); // not too simple or busy
        noiseArrays.fields[n].z[y][x] = noise(noiseY, noiseX); // save this value
      }
    }
  }

  NoiseCurves = new AUCurve[height][width];
  for (int y=0; y<height; y++) { // build a curve for each pixel
    for (int x=0; x<width; x++) {
      float[][] knots = new float[numFields][1];  // the data for this curve
      for (int n=0; n<numFields; n++) { // fill in with this curve's data
        knots[n][0] = noiseArrays.fields[n].z[y][x];
      }
      NoiseCurves[y][x] = new AUCurve(knots, 1, true); // 1D geometry, closed curve
    }
  }
}

void draw() {
  Stepper.step();
  float alfa = Stepper.getFullAlfa();  // how far we are into the curves
  loadPixels();
  for (int y=0; y<height; y++) {
    for (int x=0; x<width; x++) {
      float v = NoiseCurves[y][x].getX(alfa);  // get value from this curve
      pixels[(y*width)+x] = color(255 * v); // save noise value as a shade of gray
    }
  }
  updatePixels();
}

Leave a Reply

Your email address will not be published. Required fields are marked *