Skip to content

Instantly share code, notes, and snippets.

@wschutzer
Last active October 24, 2024 14:19
Show Gist options
  • Save wschutzer/74e5f09d48026eaf9767407669ba9600 to your computer and use it in GitHub Desktop.
Save wschutzer/74e5f09d48026eaf9767407669ba9600 to your computer and use it in GitHub Desktop.
Canyon with texture
// Canyon with texture
//
// Processing code and Mathematics by Waldeck Schutzer (@infinitymathart)
// Based on code by Etienne Jacob (@etinjcb)
// Motion blur template by @davebeesandbombs, explanation/article: https://bleuje.com/tutorial6/
// OpenSimplexNoise.pde can be found here: https://gist.github.com/wschutzer/4be8f14d12fc3541024f796ee7fb6fe2
// See the license information at the end of this file.
final boolean recording = false;
final boolean reverse = false;
PShape surf;
PGraphics tex;
PFont mono;
float fac = 1;
float R = 1000;
int tube_radius = 200;
final int v_divs = 120;
final int u_divs = 600;
final color fg_color = color(255,255,255);
final color bg_color = color(0,0,0);
final color surf_color = color(0,0,0);
float tscl = 1.0; // Text scale
final int num_stripes = 40;
final float d_hover = 1.0; // text goes slightly above the surface (not needed if surface mesh very thin)
final int num_digits_v = 19; // Quantity of digits along the v direction
final Coordinates global_coords = new Coordinates( // System coordinates (global)
new PVector(0, 0, 0),
new PVector(0, -1, 0),
new PVector(1, 0, 0),
new PVector(0, 0, 1)
);
OpenSimplexNoise noise;
//////////////////////////////////////////////////////////////////////////////
// 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
//-----------------------------------
// some generally useful functions...
float c01(float x)
{
return constrain(x, 0, 1);
}
// 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);
}
// defines a map function variant to constrain or not in target interval (exists in openFrameworks)
float map(float x, float a, float b, float c, float d, boolean constr)
{
return constr ? constrain(map(x, a, b, c, d), min(c, d), max(c, d)) : map(x, a, b, c, d);
}
// short one to map an x from [a,b] to [0,1] and constrain
float mp01(float x, float a, float b)
{
return map(x, a, b, 0, 1, true);
}
// reversed pow that does some kind of ease out, [0,1] -> [0,1], with a parameter g
float pow_(float p, float g)
{
return 1-pow(1-p, g);
}
// hyperbolic tangent, maps ]-infinity,+infinity[ to ]-1,1[
float tanh(float x)
{
return (float)Math.tanh(x);
}
//-----------------------------------
void draw()
{
if (!recording) // test mode...
{
t = (mouseX*1.3/width)%1;
t += 0.007;
t%=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)%1;
// t %= 1;
draw_();
loadPixels();
for (int i=0; i<pixels.length; i++) {
result[i][0] += constrain(red(pixels[i])*bht,0,255);
result[i][1] += constrain(green(pixels[i])*bht,0,255);
result[i][2] += constrain(blue(pixels[i])*bht,0,255);
}
}
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();
// Due to a weird flicker that has to do with the first frame not being
// rendered correctly, skip the first frame, and render past 1 frame at end.
// Does not occur if the default font is used.
if (frameCount>1 && frameCount<=numFrames) {
saveFrame("/tmp/r/fr###.png");
println(frameCount, "/", numFrames);
}
if (frameCount==numFrames+1)
exit();
}
}
// End of template
//////////////////////////////////////////////////////////////////////////////
final int samplesPerFrame = 4;
final int numFrames = 4*120;
final float shutterAngle = 1.2;
final float bht = 1.1; // Brightness adjustment
class Coordinates
{
PVector position;
PVector u1, u2, u3;
Coordinates(PVector pos, PVector u1_, PVector u2_, PVector u3_)
{
position = pos;
u1 = u1_.copy().normalize();
u2 = u2_.copy().normalize();
u3 = u3_.copy().normalize();
}
// Matrix whose columns are the basis vectors
PMatrix3D getBasisMatrix() {
return new PMatrix3D(
u1.x, u2.x, u3.x, 0,
u1.y, u2.y, u3.y, 0,
u1.z, u2.z, u3.z, 0,
0, 0, 0, 1
);
}
// Computes the change of basis matrix from this basis to the other basis
PMatrix3D basisChange(Coordinates other) {
PMatrix3D inv = other.getBasisMatrix();
inv.invert(); // Computes the inverse of other matrix
PMatrix3D result = getBasisMatrix();
result.apply(inv); // Multiplies the current matrix by the inverse of other
return result;
}
}
// This defines a (closed) space curve
PVector spaceCurve(float u)
{
float theta = TAU*u; // u in the interval [0, 1]
float x = cos(theta);
float y = sin(theta);
float z = 0;
return new PVector(x, y, z).mult(R);
}
// Computes a (non-unit) tangent vector to the curve - numerical approximation
PVector tangent_vector(float u)
{
final float h = 0.001;
return PVector.sub(spaceCurve(u+h),spaceCurve(u-h)).div(2*h);
}
// Computes a (non-unit) normal vector to the curve - numerical approximation
PVector normal_vector(float u)
{
final float h = 0.001;
return tangent_vector(u+h).cross(tangent_vector(u-h));
}
// This computes the Frenet-Serret frame
Coordinates FrenetBasis(float u)
{
PVector pos = spaceCurve(u);
PVector u1 = tangent_vector(u); // T vector (tangent)
PVector u2 = normal_vector(u); // N vector (normal)
PVector u3 = u2.copy().cross(u1); // B vector (binormal)
return new Coordinates(pos, u1, u2, u3);
}
// This creates a circular tube enclosing the curve
PVector s(float u, float v, float r)
{
final float nsu = 10.0;
final float nsv = 4.0;
final float w = 2*TAU;
final float b = 3*PI/4;
float rs = r*(1+0.3*(float)noise.eval(nsu*cos(w*u), nsu*sin(w*u), nsv*cos(b*v), nsv*sin(b*v)));
float d1 = rs*cos(b*v+PI/8);
float d2 = rs*sin(b*v+PI/8);
Coordinates coords = FrenetBasis(u);
// The cross-section of the surface is a circle in the (N, B)-plane:
PVector crossSection = PVector.add(coords.u2.copy().mult(d1),coords.u3.copy().mult(d2));
return PVector.add(coords.position, crossSection);
}
// Tangent vector to the surface along the u direction
PVector dsdu(float u, float v, float r)
{
final float h = 0.001;
return PVector.sub( s(u+h,v,r), s(u-h,v,r) ).div(2*h);
}
// Tangent vector to the surface along the v direction
PVector dsdv(float u, float v, float r)
{
final float h = 0.001;
return PVector.sub( s(u,v+h,r), s(u,v-h,r) ).div(2*h);
}
// Normal vector to the surface at a point
PVector normal_s(float u, float v, float r)
{
return dsdv(u,v,r).cross( dsdu(u,v,r) );
}
// Compute the surface geometry
PShape createSurface()
{
noFill();
noStroke();
PShape s = createShape();
s.beginShape(TRIANGLES);
s.fill(255);
s.textureMode(NORMAL);
s.texture(tex);
for (int i=0; i<=u_divs; i++)
{
float u1 = map(i, 0, u_divs, 0, 1);
float u2 = map(i+1, 0, u_divs, 0, 1);
for (int j=0; j<v_divs; j++)
{
float v = map(j, 0, v_divs, 0, 1);
float vn = map(j+1, 0, v_divs, 0, 1);
PVector v1 = s(u1, v, tube_radius);
PVector n1 = normal_s(u1, v, tube_radius);
PVector v2 = s(u2, v, tube_radius);
PVector n2 = normal_s(u2, v, tube_radius);
PVector v3 = s(u1, vn, tube_radius);
PVector n3 = normal_s(u1, vn, tube_radius);
PVector v4 = s(u2, vn, tube_radius);
PVector n4 = normal_s(u2, vn, tube_radius);
s.normal(n1.x,n1.y,n1.z);
s.vertex(v1.x, v1.y, v1.z, u1, v);
s.normal(n2.x,n2.y,n2.z);
s.vertex(v2.x, v2.y, v2.z, u2, v);
s.normal(n4.x,n4.y,n4.z);
s.vertex(v4.x,v4.y,v4.z, u2, vn);
s.normal(n1.x,n1.y,n1.z);
s.vertex(v1.x, v1.y, v1.z, u1, v);
s.normal(n4.x,n4.y,n4.z);
s.vertex(v4.x,v4.y,v4.z, u2, vn);
s.normal(n3.x,n3.y,n3.z);
s.vertex(v3.x,v3.y,v3.z, u1, vn);
}
}
s.endShape();
return s;
}
// Draw the stored surface
void drawSurface()
{
surf.setTextureMode(NORMAL);
surf.setFill(color(255));
surf.resetMatrix();
surf.setTexture(tex);
pushMatrix();
rotateZ(-PI*t);
shape(surf); // This is a lot faster than the immediate mode
popMatrix();
}
void settings()
{
if (recording)
size(2160,2160,P3D);
else
size(600, 600, P3D);
fac = width/600.0;
R *= fac;
tube_radius *= fac;
smooth(8);
}
void setup()
{
tex = createGraphics(2*width, 2*height);
noise = new OpenSimplexNoise(1337);
drawTexture();
surf = createSurface();
result = new int[width*height][3];
// Some fonts tested.
// mono = createFont("AkayaKanadaka", 144);
// mono = createFont("American Typewriter", 144);
// mono = createFont("Andale Mono", 144); // Very Nice
// mono = createFont("Annai MN", 144); tscl = 0.7;// Interesting
// mono = createFont("Arial Rounded MT Bold", 144);
mono = createFont("Bai Jamjuree", 144); tscl = 0.9;
textFont(mono);
}
void draw_()
{
drawTexture();
background(reverse ? fg_color : bg_color);
lights();
//lightFalloff(2.0, 0.005, 0.001);
directionalLight(200, 200, 200, 0, -1, -1);
camera(width/2.0, height/2.0, (height/2.0) / tan(PI*23.0 / 180.0), width/2.0, height/2.0, 0, 0, 1, 0);
push();
translate(width/2, height/2); // -fac*20);
scale(1.2);
rotateZ(HALF_PI);
rotateX(-HALF_PI);
translate(-fac*20-R-0.4*tube_radius, -0.4*R, 0);
drawSurface();
pop();
drawSignature();
}
void drawTexture()
{
int s_len = int(tex.width/(3*num_stripes));
tex.beginDraw();
tex.background(reverse ? 255 : 0);
tex.noStroke();
tex.fill(reverse? 20 : 255);
for(int i=-2*s_len/*-int(2*s_len*t)*/; i<tex.width+2*s_len; i+= 3*s_len)
{
tex.rect(0, i, tex.width, s_len);
}
tex.endDraw();
//if (frameCount==1)
// tex.save("/tmp/frame.png");
}
void drawSignature()
{
fill(reverse ? bg_color : fg_color);
push();
translate(width/2,height/2,0);
translate(0,height*0.63,0);
scale(0.14*fac);
textAlign(CENTER);
text("@infinitymathart | 2024", 0, 0, 0);
pop();
}
/* License:
*
* Copyright (c) 2024 Waldeck Schutzer
*
* 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