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.
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);
}