• fullscreen
  • NoiseBasedWarping.pde
  • _1_DomainWarper.pde
  • _2_DomainStrategy.pde
  • /*--------------------------------------------------------------------------------------------------------------------------------
    Noise Based Warping Explorer
    Ale González · 2012 · Dominio Público | Public Domain
    ----------------------------------------------------------------------------------------------------------------------------------
    A explorer of noise based warping, as explained here: http://www.iquilezles.org/www/articles/warp/warp.htm
    And as pointed out here: http://amnonp5.wordpress.com/2012/06/17/playing-with-glsl-in-processing/
    ----------------------------------------------------------------------------------------------------------------------------------
      
    The process as described basically by Iñigo:
    "Say you have some geometry or image defined as a function of space. For geometry, that would be a function of the form f(x,y,z) 
    and for an image it would be f(x,y). We can just write both cases more compactly as f(p), where p is the position in space for 
    which we can evaluate the volumetric density that will define our (iso)surface or image color. Warping simply means we distort
    the domain with another function g(p) before we evaluate f. Basically, we replace f(p) with f(g(p)). g can be anything, but often 
    we want to distort the image of f just a little bit with respect its regular behabiour. Then, it makes sense to have g(p) being 
    just the identiy plus a small arbitary distortion h(p), or in other words,
    
    g(p) = p + h(p),
    
    meaning we will be computing
    
    f( p + h(p) )
    
    This technique is really powerful and allows you to shape apples, buildings, animals or any other thing you might imagine. For the 
    purpose of this article, we are going to work only with fbm based patterns, both for f ang h. This will produce some abstract but
    beautiful images with a pretty organic quality to them."
    
    ----------------------------------------------------------------------------------------------------------------------------------
    Thanks to both, Íñigo and Amnon, for dedicating so much time to share. ;-)
    ----------------------------------------------------------------------------------------------------------------------------------
    Font: 'sw!ft' by orgdot, as shared on dafont, http://www.dafont.com/orgdot.d518. Thanks again!
    --------------------------------------------------------------------------------------------------------------------------------*/
    
    
    /*
     Global variables and objects
    */
    
    //Canvas size
    final int W= 500, H= 500;
    
    //A font to output frameRate
    PFont theFont;
    
    //Default level of recursion and text properties
    int level = 1,
    txt_x = 25,
    txt_y = 25,
    txt_c = 0xddffff00,
    txt_s = 10;
    
    //default values to instantiate the noise domain and warper objects
    float 
      perlin_x= .01,
      perlin_y= .01,
      simplex_x= .0045,
      simplex_y= .0045,  
      min_V = -1f,
      max_V =  1f,
      vK=4f;
    
    boolean
      moving = false,
      hud     = true;
      
    
    DomainStrategy domain;
    DomainWarper   warper;
    
    
    /*
     Main methods
    */
    
    void setup(){
      size (W,H,P2D);  
      cursor (CROSS);
      domain  = new Perlin  (255, perlin_x, perlin_y);
      warper  = new DomainWarper (level, min_V, max_V, vK);
      textFont (loadFont("SWFTv01-16.vlw"), txt_s);
      fill (txt_c);
    }
    
    void draw(){
      loadPixels();
      if (moving) {warper.x2--; warper.y1++;}
      //Iterate over screen pixels
      for (int y=0, yW=0; y<H; y++, yW+=W) {
        for(int x=0; x<W; x++) {
           //And use domain values (v) to set grayscale values to them, using the domain function provided
          int v = (int) warper.getValue (x,y, domain);
          pixels[x+yW] = v<<16|v<<8|v; 
        }
      }
      updatePixels();
      if (hud) text ("Level: " + level + " - K: " + vK + " - Framerate: " + nfc(frameRate,2), txt_x, txt_y);
    }
    
    
    /*
     Some interaction here:
     · Move mouse to shift the warping, acting onto one of the vectors that define the parent of the tree structure
     · Keys:
       '1'    : use Perlin noise based domain
       '2'    : use Simplex noise based domain
       'Up'   : increase level of recursion (up to 5)
       'Down' : decrease level of recursion (down to 0).
    */
    
    void mousePressed(){
      switch (mouseButton) {
        case (LEFT):
          moving= !moving;
          break;
        case (RIGHT):
          hud = !hud;
          break;
      } 
    }
    
    void keyPressed(){
      if (key!=CODED) {
        switch (key) {
          case (KeyEvent.VK_1) : //key '1' constant value as implemented in KeyEvent Java class
            domain = new  Perlin (255, perlin_x, perlin_y);
            break;
          case (KeyEvent.VK_2) : //...
            domain = new Simplex (255, simplex_x, simplex_y);
            break;
        }
      } else {
        if      (keyCode==UP    && level<10) warper= new DomainWarper (++level, min_V, max_V, vK);
        else if (keyCode==DOWN  && level>1)  warper= new DomainWarper (--level, min_V, max_V, vK);
        else if (keyCode==RIGHT && vK<=10)   warper= new DomainWarper (level, min_V, max_V, ++vK);
        else if (keyCode==LEFT  && vK>=0)    warper= new DomainWarper (level, min_V, max_V, --vK);
      }
    }
    
    /*
    A domain-based compound warper. 
    Each possible level of recursion is encapsulated in a warper object inside a unary tree structure 
    */
    
    class DomainWarper {
    
      //Each warping level is 'described' by two points, defined by the vectors: v1{x1,y1} and v2{x2,y2}
      //k is a constant that scales the effect of the correspondent recursion level  
      float x1, y1, x2, y2, k;
      
      //Further depth level, only accesible from its direct parent
      private DomainWarper child;
       
       
      /*
       Constructors
      */
     
      //Create a custom warper from given parameters  
      DomainWarper (float x1, float y1, float x2, float y2, float k) {
        this.x1=x1; this.y1=y1;
        this.x2=x2; this.y2=y2;
        this.k=k;
        child = null;
      }
      
      //Create a warper of given depth, defined at each level by a given k and random vectors, 
      //using a float range minV<-->maxV.
      DomainWarper (int level, float minV, float maxV, float k){
        x1= random (minV, maxV); y1= random (minV, maxV);
        x2= random (minV, maxV); y2= random (minV, maxV);
        this.k=k;
        if(--level>0) this.addLevel (new DomainWarper(level, minV, maxV, k));  
      }
    
      /*
       Methods
      */
      
      /*
        To add recursively a new level, defined by a Warper child:
        If the node is a leaf, add it the new child, else traverse its children until a leaf is found and the warper is finally added to this one
      */
      void addLevel (DomainWarper rw) {
        if (child==null) child=rw; else child.addLevel (rw);
      }
      
      /*
        This method calculates recursively the sum of associated vectors of parent node and its children. The associated vector of a warper object is
        defined by calculating its coordinates as the result of applying the domain algorithm to p{x,y}+v1 and p+v2, scaled by the k coefficient afterwards.
      */
      PVector getLevelVector (int x, int y, DomainStrategy domain) {
        PVector levelVector = new PVector (domain.getValue (x+x1, y+y1) *k, domain.getValue (x+x2, y+y2) *k);
        if (child!=null) levelVector.add (child.getLevelVector (x, y, domain));
        return levelVector;
      }
      
      /*
        The main output of the object is this value, result of applying the domain algorithm to
        the associated vector of the parent of the tree
      */
      float getValue (int x, int y, DomainStrategy domain) {
        PVector v = getLevelVector (x, y, domain);
        return domain.getValue (x+v.x, y+v.y);
      }
    }
    
    /*
    Interface DomainStrategy.
    I have preferred to encapsulate different domain math algorithms in objects 
    implementing this interface in order to be easily extended, keeping clean 
    the warper class.
    */
    
    interface DomainStrategy {
      float getValue (float x, float y); 
    }
    
    /*
    This one uses Perlin noise as ported to P5 by Toxi from the original implementation 
    by german demo group Farbrausch: http://www.farb-rausch.de/fr010src.zip
    Original implementation: Ken Perlin, 1984
    */
    
    class Perlin implements DomainStrategy {
      
      float magnitude, kX, kY;
      
      Perlin (float magnitude, float kX, float kY) {
        this.magnitude   = magnitude;
        this.kX = kX; this.kY = kY;
      }
      
      float getValue (float x, float y) {
        return magnitude * noise (kX*x, kY*y);   
      } 
    }
    
    /*
    Simplex noise as implemented by Toxi in his Math library, and as described in 
    this paper by Stefan Gustavson: http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf.
    Original implementation: Ken Perlin, 2001
      http://mrl.nyu.edu/~perlin/paper445.pdf    Siggraph 2002 paper explaining the new algorithm
      http://mrl.nyu.edu/~perlin/noise/          the raw code -- quite hardcoded, compact, beautiful... and absolutely impenetrable :-)
    */
    
    import toxi.math.noise.SimplexNoise;
    
    class Simplex implements DomainStrategy {
      
      float magnitude, kX, kY;
      
      Simplex (float magnitude, float kX, float kY) {
        this.magnitude   = magnitude;
        this.kX = kX; this.kY = kY;
      }
      
      float getValue (float x, float y) {
        //Simplex yields values from -1d to 1d unlike standard P5 noise implementation (that gives values from 0 to 1f), 
        //so this object yields the absolute value casted to float:
        return  magnitude * abs((float) SimplexNoise.noise(kX*x, kY*y));   
      }
    } 
    

    code

    tweaks (0)

    about this sketch

    This sketch is running as Java applet, exported from Processing.

    license

    advertisement

    ale plus+

    NoiseBasedWarping_Explorer

    Add to Faves Me Likey@! 15
    You must login/register to add this sketch to your favorites.

    This sketch tries to go deep into noise based warping, a technique described by Iñigo Quilez here:
    http://www.iquilezles.org/www/articles/warp/warp.htm
    and pointed out by Amnon Owed here:
    http://amnonp5.wordpress.com/2012/06/17/playing-with-glsl-in-processing/
    (Thanks to both)

    I've done the sketch as an exercise that tries to understand the technique and to observe the effect of the different parameters involved in the final result. I've designed it with this objective in mind, so it isn't a much efficient design for real time animations.

    ale plus+
    1 Sep 2012
    Interaction:
    - Key UP -- Add a domain
    - Key DOWN -- Remove it
    - Key RIGHT -- Increase the coeficient that scales the effect of each domain into the whole
    - Key LEFT -- Decrease it
    - Key 1 -- Standard P5 Perlin noise
    - Key 2 -- Simplex Noise
    - Mousebutton LEFT -- Toggle animating the vectors that define the domain
    - Mousebutton RIGHT -- Toggle showing the Info

    Conclusions:
    - If you want to get rich effects for real time is best to increase the coefficient (vK in the Code) instead of increasing the level of complexity. Maths are very expensive here.
    - Simplex is by far more efficient than simple noise but the effect is less interesting in organic terms.
    - For real-time is better to use a LUT approach, storing in a PVector[level][x][y] all level vectors. This increases x2 the framerate although is quite expensive in memory terms, so it isn't a useful approach (for instance) to get hi-res pictures. It'd be a good exercise to create a general-purpose design for the class Warper.

    Font: 'sw!ft' by orgdot, as shared on dafont, http://www.dafont.com/orgdot.d518. Thanks again!
    R.A. Robertson
    4 Sep 2012
    Absolutely amazing.

    This is exactly the kind of terrain I've been trying to explore lately, without making any progress.
    I'll be studying this.

    Thank you.
    ale plus+
    11 Sep 2012
    You're welcome!
    :-)
    You need to login/register to comment.