Animated distortion gifs from a vector field and a scalar field

This short tutorial explains how to obtain this kind of effect on images with Processing :

agif.gif

I advice to read the stripes tutorial first because this is kind of an extension to it.

This is the photograph we’ll work with :

water.jpg

First of all let’s distort an image without animation. To do that we’ll use a vector field that indicates for each pixel of the resulting image which pixel from the original image to take. We’ll use a vector field based on Perlin noise :

PVector vector_field(float x,float y){
  float amount = 100;
  float scale = 0.01;
  return new PVector(amount*(noise(scale*x,scale*y)-0.5),4*amount*(noise(100+scale*x,scale*y)-0.5));
}

The “+100” is there to have independant noise values from the first calculated ones. The “-0.5” are there because noise values are centered on 0.5.

Here is the complete code achieve the static distortion :

int numFrames = 25;

color[] result;
PImage img;

PVector vector_field(float x,float y){
  float amount = 100;
  float scale = 0.01;
  return new PVector(amount*(noise(scale*x,scale*y)-0.5),4*amount*(noise(100+scale*x,scale*y)-0.5));
}

PVector distortion(float x,float y,float time){
  PVector res = vector_field(x,y);
  return res;
}

void setup() {
  img = loadImage("water.jpg");
  size(370,254);
  
  result = new color[width*height];
}

void draw(){
  // Time variable
  float t = 1.0*(frameCount-1)%numFrames/numFrames;
  
  image(img,0,0);
  
  // Renders the pixels using the distortion field
  loadPixels();
  for (int i=0; i<width; i++) {
    for(int j=0;j<height;j++){
      PVector res = distortion(i,j,t);
      
      int ii = constrain(floor(i + res.x),0,width-1);
      int jj = constrain(floor(j + res.y),0,height-1);
      
      result[i + width*j] = pixels[ii + width*jj];
    }
  }
  for (int i=0; i<width; i++) {
    for(int j=0;j<height;j++){
      pixels[i + width*j] = result[i + width*j];
    }
  }
  updatePixels();
  
  // Saves the frame
  println(frameCount,"/",numFrames);
  saveFrame("frame###.png");
  
  // Stops when all the frames are rendered
  if(frameCount==numFrames){
    println("finished");
    stop();
  }
}

Result :

frame025.png

The position of the pixel from which we take the color is constrained to avoid having an out of bounds error :

      int ii = constrain(floor(i + res.x),0,width-1);
      int jj = constrain(floor(j + res.y),0,height-1);

An array “result” is used to avoid ovewriting on original pixels that must be used to compute other resulting pixels.

Now to animate it, we’ll have moving stripes over the image activating and desactivating the distortion, that’s why I advice to understand the stripes tutorial first.

Here is the added code :

float scalar_field(float x,float y){
  return 0.03*y;
}

PVector distortion(float x,float y,float time){
  PVector res = vector_field(x,y);
  
  float intensity = map(sin(TWO_PI*(time+scalar_field(x,y))),-1,1,0,1);
  
  res.mult(intensity);
  
  return res;
}

It should be easy to understand if you understood the stripes tutorial, because here the white stripes correspond to the use of the distortion and black stripes to no distortion.

Here is the result :

agif2.gif

Now we can try other vector and scalar fields :

PVector vector_field(float x,float y){
  float amount = 50;
  float scale = 0.01;
  return new PVector(amount*(noise(scale*x,scale*y)-0.5),amount*(noise(100+scale*x,scale*y)-0.5));
}

float scalar_field(float x,float y){
  return 0.05*dist(x,y,width/2,height/2);
}

Result :

agif3.gif

By reducing the intensity of the distortion away from the center :

PVector vector_field(float x,float y){
  float amount = 150;
  float scale = 0.01;
  float intensity = constrain(map(dist(x,y,width/2,height/2),0,0.75*height,1,0),0,1);
  return new PVector(amount*(noise(scale*x,scale*y)-0.5)*intensity,amount*(noise(100+scale*x,scale*y)-0.5)*intensity);
}

float scalar_field(float x,float y){
  return -0.05*dist(x,y,width/2,height/2);
}

Result :

agif4.gif

That is all, I hope things were quite clear and that this tutorial will inspire you to make something (maybe much better and interesting) yourself.

Here is the complete code to generate the last GIF in case you missed anything…

int numFrames = 25;

color[] result;
PImage img;

PVector vector_field(float x,float y){
  float amount = 150;
  float scale = 0.01;
  float intensity = constrain(map(dist(x,y,width/2,height/2),0,0.75*height,1,0),0,1);
  return new PVector(amount*(noise(scale*x,scale*y)-0.5)*intensity,amount*(noise(100+scale*x,scale*y)-0.5)*intensity);
}

float scalar_field(float x,float y){
  return -0.05*dist(x,y,width/2,height/2);
}

PVector distortion(float x,float y,float time){
  PVector res = vector_field(x,y);
  
  float intensity = map(sin(TWO_PI*(time+scalar_field(x,y))),-1,1,0,1);
  
  res.mult(intensity);
  
  return res;
}

void setup() {
  img = loadImage("water.jpg");
  size(370,254);
  
  result = new color[width*height];
}

void draw(){
  // Time variable
  float t = 1.0*(frameCount-1)%numFrames/numFrames;
  
  image(img,0,0);
  
  // Renders the pixels using the distortion field
  loadPixels();
  for (int i=0; i<width; i++) {
    for(int j=0;j<height;j++){
      PVector res = distortion(i,j,t);
      
      int ii = constrain(floor(i + res.x),0,width-1);
      int jj = constrain(floor(j + res.y),0,height-1);
      
      result[i + width*j] = pixels[ii + width*jj];
    }
  }
  for (int i=0; i<width; i++) {
    for(int j=0;j<height;j++){
      pixels[i + width*j] = result[i + width*j];
    }
  }
  updatePixels();
  
  // Saves the frame
  println(frameCount,"/",numFrames);
  saveFrame("frame###.png");
  
  // Stops when all the frames are rendered
  if(frameCount==numFrames){
    println("finished");
    stop();
  }
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s