Skip to content

Instantly share code, notes, and snippets.

@Bleuje
Created September 17, 2024 20:00
Show Gist options
  • Save Bleuje/04348a2b692737d69120d9858e8d8f4a to your computer and use it in GitHub Desktop.
Save Bleuje/04348a2b692737d69120d9858e8d8f4a to your computer and use it in GitHub Desktop.
// Processing code by Etienne Jacob
// motion blur template by beesandbombs, explanation/article: https://bleuje.com/tutorial6/
// See the license information at the end of this file.
//////////////////////////////////////////////////////////////////////////////
// Start of template
int[][] result; // pixel colors buffer for motion blur
float t; // time global variable in [0,1[
float c; // other global variable for testing things, controlled by mouse
//-----------------------------------
// ease in and out, [0,1] -> [0,1], with a parameter g:
// https://patakk.tumblr.com/post/88602945835/heres-a-simple-function-you-can-use-for-easing
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);
}
//-----------------------------------
void draw()
{
if (!recording) // test mode...
{
t = (mouseX*1.3/width)%1;
c = mouseY*1.0/height;
if (mousePressed)
println(c);
draw_();
}
else // render mode...
{
for (int i=0; i<width*height; i++)
for (int a=0; a<3; a++)
result[i][a] = 0;
c = 0;
for (int sa=0; sa<samplesPerFrame; sa++) {
t = map(frameCount-1 + sa*shutterAngle/samplesPerFrame, 0, numFrames, 0, 1);
t %= 1;
draw_();
loadPixels();
for (int i=0; i<pixels.length; i++) {
result[i][0] += red(pixels[i]);
result[i][1] += green(pixels[i]);
result[i][2] += blue(pixels[i]);
}
}
loadPixels();
for (int i=0; i<pixels.length; i++)
pixels[i] = 0xff << 24 |
int(result[i][0]*1.0/samplesPerFrame) << 16 |
int(result[i][1]*1.0/samplesPerFrame) << 8 |
int(result[i][2]*1.0/samplesPerFrame);
updatePixels();
if (frameCount<=numFrames) {
saveFrame("data/fr###.gif");
println(frameCount,"/",numFrames);
}
if (frameCount==numFrames)
stop();
}
}
// End of template
//////////////////////////////////////////////////////////////////////////////
int samplesPerFrame = 1;
int numFrames = 150;
float shutterAngle = 1.0;
boolean recording = false;
float L = 500; // size parameter for the entire thing
int MAX_DEPTH = 5;
// Function for drawing a white square,
// controlling how far the lines go into the inside of the square (lines are actually drawn rectangles)
// instead of using strokeWeight and line functions.
// Different drawing depending on depth index
void drawWhiteSquare(int depthIndex)
{
float l = map(pow(depthIndex,0.8), 0, pow(MAX_DEPTH,0.8), 0.02*L, L/2); // replaces the strokeWeight
// going towards full white square at max depth
// basically just starts small and ends at L/2 to fill the square, the rest is experimentation for aesthetic
fill(255);
noStroke();
for(int i=0;i<4;i++)
{
// draw side i...
push();
rotate(HALF_PI*i);
// just drawing a white rectangle:
beginShape();
vertex(-L/2,-L/2);
vertex(L/2,-L/2);
vertex(L/2,-L/2+l);
vertex(-L/2,-L/2+l);
endShape();
pop();
}
}
void drawFractal(int depthIndex, float currentDepthTime)
{
currentDepthTime = (1234 + currentDepthTime)%1; // loop time, keep it in [0,1[
// adding 1234 is a habit to make sure we take the modulo 1 of a positive thing
if(depthIndex==MAX_DEPTH)
{
drawWhiteSquare(depthIndex);
}
else
{
scale(1.0/3.0);
drawWhiteSquare(depthIndex); // draw a square at this depth, then we recursively draw the stuff around the square
// then we recursively draw the stuff around the square
int K = 5; // replacement technique number (https://bleuje.com/tutorial4/)
for(int i=0;i<K;i++)
{
float replacementTechniqueProgress = (i + currentDepthTime)/K;
float drawingProgress = replacementTechniqueProgress; // this is kind of the progress that will be used for choosing drawing position of the i-th square
if((depthIndex%2)==1) drawingProgress = 1 - drawingProgress; // alternating progress direction every other depth index
drawingProgress = (1234 + drawingProgress - 0.25*depthIndex)%1; // progress offset with depth for extra layer of complexity
float floatCorner = 4 * drawingProgress;
int cornerIndex = floor(floatCorner);
float straightMovementProgress = floatCorner - cornerIndex;
straightMovementProgress = ease(straightMovementProgress,2.0);
push();
float r = 2/sqrt(2)*L;
// start corner position
float theta1 = QUARTER_PI + TAU*cornerIndex/4;
PVector pos1 = new PVector(r*cos(theta1),r*sin(theta1));
// end corner position
float theta2 = QUARTER_PI + TAU*(cornerIndex+1)/4;
PVector pos2 = new PVector(r*cos(theta2),r*sin(theta2));
// real position with interpolation of weight straightMovementProgress
PVector pos = pos1.copy().lerp(pos2, straightMovementProgress);
translate(pos.x,pos.y);
scale(1.0 - 0.4*sin(straightMovementProgress*PI)); // smaller scale in the middle of movement
// because of replacement technique, the motion at next depth level should be done a lot of times for a full current depth replacement technique cycle
// or it's going to be very slow
// Normal (same speed at all depths):
//float nextTime = K * replacementTechniqueProgress;
// Faster at next depth:
float nextTime = 8 * (depthIndex + 1) * replacementTechniqueProgress;
// as long as nextTime starts at 0 for progress=0 and ends at any integer n for progress=1, it will loop perfectly so it's possible to experiment easily
drawFractal(depthIndex + 1, nextTime);
pop();
}
}
}
void setup()
{
size(600,600,P3D);
result = new int[width*height][3];
smooth(8);
}
void draw_()
{
background(0);
push();
translate(width/2,height/2);
drawFractal(0,t);
pop();
}
/* License:
*
* Copyright (c) 2024 Etienne Jacob
*
* All rights reserved.
*
* This code after the template and the related animations are the property of the
* copyright holder. Any reproduction, distribution, or use of this material,
* in whole or in part, without the express written permission of the copyright
* holder is strictly prohibited.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment