Created
November 27, 2024 02:16
-
-
Save wschutzer/b19c79ccb24acd77759eec753ccd0d1b to your computer and use it in GitHub Desktop.
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
/* Love Equation | |
* --------------------- | |
* | |
* Copyright (C) 2024 Waldeck Schutzer (@infinitymathart) | |
* | |
* This program is free software: you can redistribute it and/or modify | |
* it under the terms of the GNU General Public License as published by | |
* the Free Software Foundation, either version 3 of the License, or | |
* (at your option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program. If not, see <https://www.gnu.org/licenses/>. | |
*/ | |
final boolean recording = false; | |
final int duration = 8; // Seconds | |
final int fps = 60; // Frames Per Second | |
final int num_frames = fps*duration; | |
// Motion blur: | |
final int samples_per_frame = 10; // How many frames to average? | |
final float shutter_angle = 1.5; // How distant the frames are in time? | |
final float aspect = 1920.0/1080; | |
final int frameWidth = recording ? 2160 : 400; | |
final int frameHeight = (int)(aspect*frameWidth); | |
final float fac = 1.0*frameWidth/400; | |
float t = 0; // Normalized animation time in the interval 0 - 1. | |
float k = 0; | |
PShader motionBlur; | |
PGraphics gtex0; // Each frame is drawn here | |
// Vertex shader code | |
String[] motionBlurVertShader = { | |
"#version 100", | |
"#define PROCESSING_TEXTURE_SHADER", | |
"uniform mat4 transformMatrix;", | |
"uniform mat4 texMatrix;", | |
"attribute vec4 position;", | |
"attribute vec4 color;", | |
"attribute vec2 texCoord;", | |
"varying vec4 vertColor;", | |
"varying vec4 vertTexCoord;", | |
"void main() {", | |
"gl_Position = transformMatrix * position;", | |
"vertTexCoord = texMatrix * vec4(texCoord, 1.0, 1.0);", | |
"vertColor = color;", | |
"}"}; | |
String[] motionBlurFragShader = { | |
"#version 100", | |
"#ifdef GL_ES", | |
"precision mediump float;", | |
"precision mediump int;", | |
"#endif", | |
"#define PROCESSING_TEXTURE_SHADER", | |
"uniform sampler2D texture; // Source and result frame, works as an accumulator", | |
"uniform sampler2D tex0; // Currently drawn frame to be added", | |
"uniform float alpha;", | |
"uniform float beta;", | |
"varying vec4 vertColor; // Seems to be white", | |
"varying vec4 vertTexCoord;", | |
"void main()", | |
"{", | |
" vec4 curColor = texture2D(tex0, vertTexCoord.xy); // Color of current pixel ", | |
" vec4 accColor = texture2D(texture, vertTexCoord.xy); // Color in accumulator", | |
" gl_FragColor = beta*(alpha*accColor + curColor); // Blending", | |
"}"}; | |
void settings() | |
{ | |
size(frameWidth, frameHeight, P2D); // Use P2D renderer for shader support | |
smooth(); | |
} | |
void setup() | |
{ | |
gtex0 = createGraphics(width, height, P2D); | |
gtex0.smooth(); | |
motionBlur = new PShader(this, motionBlurVertShader, motionBlurFragShader); | |
formula = loadShape("formula.svg"); | |
} | |
void draw() | |
{ | |
background(0); | |
for(int i=0; i<samples_per_frame; i++) | |
{ | |
t = map((recording ? 1.0*(frameCount-1): 1.0*mouseX/width*num_frames) + i*shutter_angle/samples_per_frame, 0, num_frames, 0, 1) % 1; | |
draw_(gtex0,t); // Draw on gtex1 | |
motionBlur.set("tex0", gtex0); | |
motionBlur.set("alpha", (float)(i)); | |
motionBlur.set("beta", (float)(1.0/(i+1))); | |
filter(motionBlur); // Averages the frames with a moving average | |
} | |
if (recording) | |
{ | |
println(frameCount+"/"+num_frames); | |
saveFrame("/tmp/f/frame_####.png"); | |
if (frameCount >= num_frames) | |
exit(); | |
} | |
} | |
// --------------------------------------------------------------------------------- | |
final int num_points = (int)(10000*fac); | |
final float hsize = 70.0*fac; | |
final float fsize = 1.3*fac; | |
float y0 = 10*fac; | |
PShape formula; | |
float ease(float p, float g) { | |
if (p < 0.5) | |
return 0.5 * pow(2*p, g); | |
else | |
return 1 - 0.5 * pow(2*(1 - p), g); | |
} | |
float easeOutElastic(float x) | |
{ | |
float c4 = (2*PI)/3; | |
if(x<=0) return 0; | |
if(x>=1) return 1; | |
return pow(2, -13 * x) * sin((x * 10 - 0.75) * c4) + 1; | |
} | |
// the heart curve | |
PVector rheart(float theta, float r) | |
{ | |
r*=0.9; | |
float s = sin(theta); | |
float x = r*s*s*s; | |
float y = r*(13*cos(theta)-5*cos(2*theta)-2*cos(3*theta)-cos(4*theta))/16; | |
return new PVector(x, -y); | |
} | |
float f(float x) | |
{ | |
if (x <= -sqrt(5) || x>= sqrt(5)) return pow(5,1.0/3); | |
return pow(x*x,1.0/3)+0.9*sqrt(5-x*x)*sin(k*PI*x); | |
} | |
void draw_(PGraphics g,float t) | |
{ | |
if (t < 0.7) | |
k = 80/PI*ease(t/0.7,5.2); | |
else | |
k = 80/PI*((1-easeOutElastic(ease((t-0.7)/0.3,3.2)))); | |
g.beginDraw(); | |
g.push(); | |
g.background(0); // Clear the canvas | |
g.noStroke(); | |
g.fill(255, 0, 0); | |
g.translate(width/2,height/2); | |
PVector p = rheart(TAU*t, 3.5*hsize); | |
g.ellipse(p.x,p.y-hsize-y0,8*fac,8*fac); | |
g.stroke(255,0,0); | |
g.strokeWeight(1.5*fac); | |
g.noFill(); | |
g.beginShape(); | |
float y2 = -hsize*f(-sqrt(5))-y0; | |
g.vertex(-width/2,y2); | |
g.vertex(-sqrt(5)*hsize,y2); | |
for(int i=0; i<num_points; i++) | |
{ | |
float tt = 2.0*sqrt(5)*i/num_points - sqrt(5); | |
float x = tt*hsize; | |
float y = -hsize*f(tt)-y0; | |
g.vertex(x,y); | |
} | |
g.vertex(sqrt(5)*hsize,y2); | |
g.vertex(width/2,y2); | |
g.endShape(); | |
formula.fill(255); | |
formula.stroke(255); | |
g.shape(formula, -fsize*formula.width/2, height/3-fsize*formula.height+10*fac, fsize*formula.width, fsize*formula.height); | |
g.fill(255,0,0); | |
g.textSize(36*fac); | |
g.textAlign(CENTER); | |
g.text(String.format("k = %-8.5f", k), 0, height/3+fsize*formula.height); | |
g.fill(255); | |
g.textSize(15*fac); | |
g.text("@infinitymathart | 2024", 0, height/2-25*fac); | |
g.pop(); | |
g.endDraw(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment