Created
April 3, 2021 03:04
-
-
Save volfegan/926452801d5342906636820b20f30674 to your computer and use it in GitHub Desktop.
2D Water Ripples effect + image underwater wobbling
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//2D Water Ripples references | |
//Coding Train https://youtu.be/BZUdGqeOD0w | |
//https://github.com/CodingTrain/website/tree/main/CodingChallenges/CC_102_WaterRipples/Processing | |
//https://web.archive.org/web/20160418004149/http://freespace.virgin.net/hugo.elias/graphics/x_water.htm | |
//A very convulated version that don't use 2D arrays, that I tried to understand | |
//http://agilerepose.weebly.com/water-ripple.html | |
//Underwater Oscillation effect | |
//https://twitter.com/stexxs/status/1250501679318999040 | |
int cols, rows; | |
float[][] rippleCurrent; //Buffer the current state of the water | |
float[][] ripplePrevious; //Data from the previous Buffer frame | |
float dampening = .99; | |
int underwaterDrift; //Xoffset for underwater oscillation effect | |
PImage img; | |
void settings() { | |
img=loadImage("cat.jpg"); | |
size(img.width, img.height, P2D); | |
println("w="+img.width+", h="+img.height); | |
} | |
void setup() { | |
cols = width; | |
rows = height; | |
rippleCurrent = new float[cols][rows]; | |
ripplePrevious = new float[cols][rows]; | |
//Initialize the ripple array | |
for (int x = 1; x < cols-1; x++) { | |
for (int y = 1; y < rows-1; y++) { | |
rippleCurrent[x][y] = 0; | |
ripplePrevious[x][y] = 0; //for a trippy animation init with: img.get(x,y) | |
} | |
} | |
image(img, 0, 0); | |
} | |
void draw() { | |
if (random(1) > .92) rippleAt(random(2, width-2), random(2, height-2)); | |
//Process Water Ripple | |
for (int x = 1; x < cols-1; x++) { | |
for (int y = 1; y < rows-1; y++) { | |
rippleCurrent[x][y] = ( | |
ripplePrevious[x-1][y] + | |
ripplePrevious[x+1][y] + | |
ripplePrevious[x][y-1] + | |
ripplePrevious[x][y+1])*.5 - rippleCurrent[x][y]; | |
rippleCurrent[x][y] = rippleCurrent[x][y] * dampening; | |
} | |
} | |
//Rendering the Water Ripple with simple shading/refraction | |
//(this could be inside the Process Water Ripple loop for +speed, but for +sanity another loop) | |
loadPixels(); | |
for (int x = 1; x < cols-1; x++) { | |
for (int y = 1; y < rows-1; y++) { | |
float Xoffset = rippleCurrent[x-1][y] - rippleCurrent[x+1][y]; | |
float Yoffset = rippleCurrent[x][y-1] - rippleCurrent[x][y+1]; | |
float Shading = (Xoffset+Yoffset)/2;//or it could be 'Xoffset' itself or 'Yoffset' or the biggest | |
//if my potato computer was powerful, underwaterDrift should be calculated every cycle | |
if (random(1) > .9) underwaterDrift = int(map(noise(x*.002, y*.001+frameCount*.015), 0, 1, -20,20)); | |
color texture = img.get(int(x+Xoffset)+underwaterDrift, int(y+Yoffset)); | |
color pixel = texture + (int)Shading; | |
int index = x + y * cols; | |
pixels[index] = pixel; | |
} | |
} | |
updatePixels(); | |
//Swap the buffers | |
float[][] temp = ripplePrevious; | |
ripplePrevious = rippleCurrent; | |
rippleCurrent = temp; | |
} | |
void rippleAt(float x_, float y_) { | |
int x0 = (int)x_; | |
int y0 = (int)y_; | |
int peakValue = (int)random(100, width|height); | |
int radius = round(random(5, 10)); | |
for (int x = x0-radius; x < x0+radius; x+=2) { | |
if (x < 1 || x > width-1) continue; | |
for (int y = y0-radius; y < y0+radius; y+=2) { | |
float distance = dist(x0, y0, x, y); | |
if (y < 1 || y > height-1 || distance > radius) continue; | |
ripplePrevious[x][y] = map(distance, 0, radius, peakValue/2, peakValue); | |
} | |
} | |
} | |
void mousePressed() { | |
rippleAt(mouseX, mouseY); | |
} | |
void mouseDragged() { | |
ripplePrevious[mouseX][mouseY] = 999; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment