Skip to content

Instantly share code, notes, and snippets.

@oveddan
Last active October 26, 2017 18:31
Show Gist options
  • Save oveddan/509d9510974a5a1494c58a0d0b648a09 to your computer and use it in GitHub Desktop.
Save oveddan/509d9510974a5a1494c58a0d0b648a09 to your computer and use it in GitHub Desktop.
Cellular Voronoi Zoetrope Generator
PShader frag;
int frames = 33;
float frameTheta = 2. * PI / frames;
int h = 520;
int w = 520;
float base = 2 * tan(frameTheta / 2.)* h;
int renderSize = ceil(base);
float sliceAspect = sin(1. / frames * 0.5 * 2 * PI) * 2.;
int frameWidth = ceil(h * sliceAspect);
void settings() {
size(w, h, P2D);
}
PGraphics pg;
void setup() {
frag = loadShader("frag.glsl");
frag.set("u_resolution", float(frameWidth), float(h));
frag.set("u_cells", float(3), float(14));
frag.set("u_miny", 0.1);
background(255);
}
String getFrameNumber(int frame) {
if (frame < 10)
return "0" + frame;
return "" + frame + "";
}
int frame = 0;
int layer = 0;
int layers = 4;
PVector blue = new PVector(0., 0., 1.);
PVector purple = new PVector(1., 0., 1.);
PVector white = new PVector(1., 1., 1.);
float[] layerBorderSizes = { 1., 0.4, 0.2, 0.07 };
float[] meatballSizes = { 1., 0.6, 0.45, 0.31 };
PVector[] layerColors = { blue, purple, blue, white };
void draw() {
if (frame > frames) {
frame = 0;
layer++;
if (layer >= layers)
noLoop();
}
noStroke();
frag.set("u_time", frame * 1.0 / frames);
frag.set("u_bordersize", layerBorderSizes[layer]);
frag.set("u_meatballsize", meatballSizes[layer]);
frag.set("u_color", layerColors[layer]);
//frag.set("u_frame", frame);
pg = createGraphics(frameWidth, h, P2D);
pg.beginDraw();
//pg.translate(-w/2, 0);
pg.filter(frag);
//pg.arc(0, 0, h*2, h*2, PI/2 - frameTheta / 2, PI/2 + frameTheta / 2);
//pg.rect(0, 0, 200, 200);
pg.endDraw();
translate(w/2, h/2);
scale(0.5);
rotate(lerp(0, -2. * PI, float(frame) / frames));
translate(-frameWidth/2, 0);
image(pg, 0,0 );
// saveFrame("frame-" + getFrameNumber(frame) + ".gif");
frame++;
}
// Created by Dan Oved
// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
// With code by by inigo quilez - iq/2013
// http://www.iquilezles.org/www/articles/voronoilines/voronoilines.htm
// and code by @patriciogv - 2015
// from: https://thebookofshaders.com/edit.php#12/metaballs.frag
#ifdef GL_ES
precision mediump float;
#endif
// uniform mat4 transform;
uniform vec2 u_resolution;
uniform float u_bordersize;
uniform float u_meatballsize;
uniform float u_time;
uniform vec2 u_cells;
uniform vec3 u_color;
uniform float u_miny;
#define PI 3.1415926535897932384626433832795
vec2 hash2( vec2 p ) {
return fract(sin(vec2(dot(p,vec2(127.1,311.7)),dot(p,vec2(269.5,183.3))))*43758.5453);
}
int frames = 33;
float totalTheta = 2. * PI / float(frames);
float minTheta = -totalTheta / 2.;
vec2 origin = vec2(u_resolution.x / u_resolution.y / 2., 1.);
vec2 getRadialCoords(vec2 point) {
float y = length(point - origin);
float xMagnitude = point.x - origin.x;
float theta = sin(xMagnitude / y);
return vec2((theta - minTheta) / totalTheta, y);
}
vec2 getWrappedPoint(vec2 point, vec2 cells) {
int x;
int y;
//vec2 result = point;
int rows = int(cells.y);
int cols = int(cells.x);
if (point.x < 0.) {
x = cols - 1;
}
else if(point.x >= float(cols)) {
x = 0;
}
else {
x = int(point.x);
}
if (point.y < 0.) {
y = rows - 1;
} else if(point.y >= float(rows)) {
y = 0;
} else {
y = int(point.y);
}
return vec2(x, y);
}
float voronoi( in vec2 x, in vec2 cells)
{
vec2 n = floor(x);
vec2 f = fract(x);
//----------------------------------
// first pass: regular voronoi
//----------------------------------
vec2 mg, mr;
float md = 8.0;
for( int j=-1; j<=1; j++ )
for( int i=-1; i<=1; i++ )
{
vec2 g = vec2(float(i),float(j));
vec2 o = hash2(getWrappedPoint(n+g, cells));
o = 0.5 + 0.5*sin(u_time * 2. * PI + 6.2831*o );
vec2 r = g + o - f;
float d = dot(r,r);
if( d<md )
{
md = d;
mr = r;
mg = g;
}
}
//----------------------------------
// second pass: distance to borders
//----------------------------------
md = 8.0;
for( int j=-2; j<=2; j++ )
for( int i=-2; i<=2; i++ )
{
vec2 g = mg + vec2(float(i),float(j));
vec2 o = hash2(getWrappedPoint(n+g, cells));
o = 0.5 + 0.5*sin(u_time * 2. * PI + 6.2831*o );
vec2 r = g + o - f;
if( dot(mr-r,mr-r)>0.00001 )
md = min( md, dot( 0.5*(mr+r), normalize(r-mr) ) );
}
return md;
}
vec2 random2( vec2 p ) {
return fract(sin(vec2(dot(p,vec2(127.1,311.7)),dot(p,vec2(269.5,183.3))))*43758.5453);
}
float meatballs(vec2 st, vec2 cells) {
vec2 i_st = floor(st);
vec2 f_st = fract(st);
float m_dist = 1.; // minimun distance
for (int j= -1; j <= 1; j++ ) {
for (int i= -1; i <= 1; i++ ) {
// Neighbor place in the grid
vec2 neighbor = vec2(float(i),float(j));
// Random position from current + neighbor place in the grid
vec2 offset = random2(getWrappedPoint(i_st + neighbor, cells));
// Animate the offset
offset = 0.5 + 0.5*sin(u_time * 2. * PI + 6.2831*offset);
// Position of the cell
vec2 pos = neighbor + offset - f_st;
// Cell distance
float dist = length(pos);
// Metaball it!
m_dist = min(m_dist, m_dist*dist);
}
}
// Draw cells
return m_dist;
}
float divider = 0.5;
float bordersize = 0.005;
void main() {
vec2 st = gl_FragCoord.xy/u_resolution.xy;
st.x *= u_resolution.x/u_resolution.y;
st = getRadialCoords(st);
float show = (1. - step(1. - bordersize, st.y)) * step(u_miny, st.y);
show *= step(0., st.x) * (1.-step(1., st.x));
float color;
if(st.y < divider) {
vec2 cells = u_cells * vec2(1., 6.);
st *= cells;
float meatball = meatballs(st, cells);
show *= 1.-step(u_meatballsize, meatball);
} else {
st *= u_cells;
float c = voronoi(st, u_cells);
show *= (1.-step(u_bordersize, c));
}
gl_FragColor = vec4(u_color, show);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment