6. Ripple Effect

Ripple Effect #

1. Revisión bibliográfica #

Dentro de la literatura se encuentran varias implementaciones para producir el efecto de onda, sin embargo para este trabajo, se decidio utilizar la implementacion de Hugo Elias. La cual consiste en el uso de dos buffers, los cuales se encargaran de guardar el estado actual y el estado del ciclo anterior del arreglo de pixeles.

Convolution

Por cada iteracion del ciclo, se calcula el valor de cada pixel, sumando el valor de sus pixeles vecinos en el ciclo anterior y dividiendolo entre dos, luego se le resta el valor del pixel actual en el buffer actual, y se le multiplica por un factor de amortiguacion.

2. Pseudocódigo #

damping = some non-integer between 0 and 1

begin loop 
    for every non-edge element:
    loop
        Buffer2(x, y) = (Buffer1(x-1,y)+
                        Buffer1(x+1,y)+
                        Buffer1(x,y+1)+
                        Buffer1(x,y-1)) / 2 - Buffer2(x,y)
 
        Buffer2(x,y) = Buffer2(x,y) * damping
    end loop

    Display Buffer2
    Swap the buffers 

end loop

3. Implementación #

ripple.vert
attribute vec3 aPosition;
attribute vec2 aTexCoord;

varying vec2 pos;

void main() {
  pos = aTexCoord;
  // flip the y axis
  pos.y = 1.0 - pos.y;

  vec4 positionVec4 = vec4(aPosition, 1.0);
  positionVec4.xy = positionVec4.xy * 2.0 - 1.0;

  gl_Position = positionVec4;
}
ripple.frag
#ifdef GL_ES
precision mediump float;
#endif

varying vec2 pos;

uniform vec2 res;
uniform sampler2D currBuff;
uniform sampler2D prevBuff;
uniform float damping;


void main() {
  // calculate pixel size
  vec2 pix = 1.0/res;
  
  // get water state
  float prev = texture2D(prevBuff, pos).r;
  
  // get previous neighbour water states
  float u = texture2D(currBuff, pos + vec2(0.0, pix.y)).r;
  float d = texture2D(currBuff, pos - vec2(0.0, pix.y)).r;
  float l = texture2D(currBuff, pos + vec2(pix.x, 0.0)).r;
  float r = texture2D(currBuff, pos - vec2(pix.x, 0.0)).r;

  // calculate the next state value
  float next = ((u + d + l + r)/2.0) - prev;
  next = next * damping;

  // output next state value
  gl_FragColor = vec4(next, next/2.0 + 0.5, 1.0, 1.0);
}
ripple.js
let rippleShader;
let currBuff, prevBuff;
const damping = 1;

function preload() {
    rippleShader = loadShader(
        '/VisualComputing2022_2/sketches/Taller3/ImageProcessing/Ripple/ripple.vert', 
        '/VisualComputing2022_2/sketches/Taller3/ImageProcessing/Ripple/ripple.frag');
}

function setup() {
    createCanvas(600, 600, WEBGL);
    pixelDensity(1);
    noSmooth();

    currBuff = createGraphics(width, height);
    currBuff.noSmooth();

    prevBuff = createGraphics(width, height);
    prevBuff.noSmooth();

    shader(rippleShader);

    rippleShader.setUniform("damping", damping);
    rippleShader.setUniform("res", [width, height]);
}

function draw() {

    stroke(255);
    if (mouseIsPressed) {
        point(mouseX - width / 2, mouseY - height / 2);
    }

    prevBuff.image(currBuff, 0, 0);
    currBuff.image(get(), 0, 0);

    rippleShader.setUniform('currBuff', currBuff);
    rippleShader.setUniform('prevBuff', prevBuff);

    rect(-width / 2, -height / 2, width, height);
}

Referencias #