init
This commit is contained in:
commit
3fa75abfb2
42 changed files with 5634 additions and 0 deletions
192
ghostty-shaders/clocks.glsl
Normal file
192
ghostty-shaders/clocks.glsl
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
// Author: Rigel rui@gil.com
|
||||
// licence: https://creativecommons.org/licenses/by/4.0/
|
||||
// link: https://www.shadertoy.com/view/lljfRD
|
||||
|
||||
#define BLACK_BLEND_THRESHOLD .4 // This is controls the dim of the screen
|
||||
const float timeMultiplier = 0.1f;
|
||||
|
||||
/*
|
||||
This was a study on circles, inspired by this artwork
|
||||
http://www.dailymail.co.uk/news/article-1236380/Worlds-largest-artwork-etched-desert-sand.html
|
||||
|
||||
and implemented with the help of this article
|
||||
http://www.ams.org/samplings/feature-column/fcarc-kissing
|
||||
|
||||
The structure is called an apollonian packing (or gasket)
|
||||
https://en.m.wikipedia.org/wiki/Apollonian_gasket
|
||||
|
||||
There is a lot of apollonians in shadertoy, but not many quite like the image above.
|
||||
This one by klems is really cool. He uses a technique called a soddy circle.
|
||||
https://www.shadertoy.com/view/4s2czK
|
||||
|
||||
This shader uses another technique called a Descartes Configuration.
|
||||
The only thing that makes this technique interesting is that it can be generalized to higher dimensions.
|
||||
*/
|
||||
|
||||
// a few utility functions
|
||||
// a signed distance function for a rectangle
|
||||
float sdfRect(vec2 uv, vec2 s) {
|
||||
vec2 auv = abs(uv);
|
||||
return max(auv.x - s.x, auv.y - s.y);
|
||||
}
|
||||
// a signed distance function for a circle
|
||||
float sdfCircle(vec2 uv, vec2 c, float r) {
|
||||
return length(uv - c) - r;
|
||||
}
|
||||
// fills an sdf in 2d
|
||||
float fill(float d, float s, float i) {
|
||||
return abs(smoothstep(0., s, d) - i);
|
||||
}
|
||||
// makes a stroke of an sdf at the zero boundary
|
||||
float stroke(float d, float w, float s, float i) {
|
||||
return abs(smoothstep(0., s, abs(d) - (w * .5)) - i);
|
||||
}
|
||||
// a simple palette
|
||||
vec3 pal(float d) {
|
||||
return .5 * (cos(6.283 * d * vec3(2., 2., 1.) + vec3(.0, 1.4, .0)) + 1.);
|
||||
}
|
||||
// 2d rotation matrix
|
||||
mat2 uvRotate(float a) {
|
||||
return mat2(cos(a), sin(a), -sin(a), cos(a));
|
||||
}
|
||||
// circle inversion
|
||||
vec2 inversion(vec2 uv, float r) {
|
||||
return (r * r * uv) / vec2(dot(uv, uv));
|
||||
}
|
||||
// seeded random number
|
||||
float hash(vec2 s) {
|
||||
return fract(sin(dot(s, vec2(12.9898, 78.2333))) * 43758.5453123);
|
||||
}
|
||||
|
||||
// this is an algorithm to construct an apollonian packing with a descartes configuration
|
||||
// remaps the plane to a circle at the origin and a specific radius. vec3(x,y,radius)
|
||||
vec3 apollonian(vec2 uv) {
|
||||
// the algorithm is recursive and must start with a initial descartes configuration
|
||||
// each vec3 represents a circle with the form vec3(centerx, centery, 1./radius)
|
||||
// the signed inverse radius is also called the bend (refer to the article above)
|
||||
vec3 dec[4];
|
||||
// a DEC is a configuration of 4 circles tangent to each other
|
||||
// the easiest way to build the initial one it to construct a symetric Steiner Chain.
|
||||
// http://mathworld.wolfram.com/SteinerChain.html
|
||||
float a = 6.283 / 3.;
|
||||
float ra = 1. + sin(a * .5);
|
||||
float rb = 1. - sin(a * .5);
|
||||
dec[0] = vec3(0., 0., -1. / ra);
|
||||
float radius = .5 * (ra - rb);
|
||||
float bend = 1. / radius;
|
||||
for (int i = 1; i < 4; i++) {
|
||||
dec[i] = vec3(cos(float(i) * a), sin(float(i) * a), bend);
|
||||
// if the point is in one of the starting circles we have already found our solution
|
||||
if (length(uv - dec[i].xy) < radius) return vec3(uv - dec[i].xy, radius);
|
||||
}
|
||||
|
||||
// Now that we have a starting DEC we are going to try to
|
||||
// find the solution for the current point
|
||||
for (int i = 0; i < 7; i++) {
|
||||
// find the circle that is further away from the point uv, using euclidean distance
|
||||
int fi = 0;
|
||||
float d = distance(uv, dec[0].xy) - abs(1. / dec[0].z);
|
||||
// for some reason, the euclidean distance doesn't work for the circle with negative bend
|
||||
// can anyone with proper math skills, explain me why?
|
||||
d *= dec[0].z < 0. ? -.5 : 1.; // just scale it to make it work...
|
||||
for (int i = 1; i < 4; i++) {
|
||||
float fd = distance(uv, dec[i].xy) - abs(1. / dec[i].z);
|
||||
fd *= dec[i].z < 0. ? -.5 : 1.;
|
||||
if (fd > d) {
|
||||
fi = i;
|
||||
d = fd;
|
||||
}
|
||||
}
|
||||
// put the cicle found in the last slot, to generate a solution
|
||||
// in the "direction" of the point
|
||||
vec3 c = dec[3];
|
||||
dec[3] = dec[fi];
|
||||
dec[fi] = c;
|
||||
// generate a new solution
|
||||
float bend = (2. * (dec[0].z + dec[1].z + dec[2].z)) - dec[3].z;
|
||||
vec2 center = vec2((2. * (dec[0].z * dec[0].xy
|
||||
+ dec[1].z * dec[1].xy
|
||||
+ dec[2].z * dec[2].xy)
|
||||
- dec[3].z * dec[3].xy) / bend);
|
||||
|
||||
vec3 solution = vec3(center, bend);
|
||||
// is the solution radius is to small, quit
|
||||
if (abs(1. / bend) < 0.01) break;
|
||||
// if the solution contains the point return the circle
|
||||
if (length(uv - solution.xy) < 1. / bend) return vec3(uv - solution.xy, 1. / bend);
|
||||
// else update the descartes configuration,
|
||||
dec[3] = solution;
|
||||
// and repeat...
|
||||
}
|
||||
// if nothing is found we return by default the inner circle of the Steiner chain
|
||||
return vec3(uv, rb);
|
||||
}
|
||||
|
||||
vec3 scene(vec2 uv, vec4 ms) {
|
||||
vec2 ci = vec2(.0);
|
||||
|
||||
// drag your mouse to apply circle inversion
|
||||
if (ms.y != -2. && ms.z > -2.) {
|
||||
uv = inversion(uv, cos(radians(60.)));
|
||||
ci = ms.xy;
|
||||
}
|
||||
|
||||
// remap uv to appolonian packing
|
||||
vec3 uvApo = apollonian(uv - ci);
|
||||
|
||||
float d = 6.2830 / 360.;
|
||||
float a = atan(uvApo.y, uvApo.x);
|
||||
float r = length(uvApo.xy);
|
||||
|
||||
float circle = sdfCircle(uv, uv - uvApo.xy, uvApo.z);
|
||||
|
||||
// background
|
||||
vec3 c = length(uv) * pal(.7) * .2;
|
||||
|
||||
// drawing the clocks
|
||||
if (uvApo.z > .3) {
|
||||
c = mix(c, pal(.75 - r * .1) * .8, fill(circle + .02, .01, 1.)); // clock
|
||||
c = mix(c, pal(.4 + r * .1), stroke(circle + (uvApo.z * .03), uvApo.z * .01, .005, 1.)); // dial
|
||||
|
||||
float h = stroke(mod(a + d * 15., d * 30.) - d * 15., .02, 0.01, 1.);
|
||||
c = mix(c, pal(.4 + r * .1), h * stroke(circle + (uvApo.z * .16), uvApo.z * .25, .005, 1.0)); // hours
|
||||
|
||||
float m = stroke(mod(a + d * 15., d * 6.) - d * 3., .005, 0.01, 1.);
|
||||
c = mix(c, pal(.45 + r * .1), (1. - h) * m * stroke(circle + (uvApo.z * .15), uvApo.z * .1, .005, 1.0)); // minutes,
|
||||
|
||||
// needles rotation
|
||||
vec2 uvrh = uvApo.xy * uvRotate(sign(cos(hash(vec2(uvApo.z)) * d * 180.)) * d * iTime * timeMultiplier * (1. / uvApo.z * 10.) - d * 90.);
|
||||
vec2 uvrm = uvApo.xy * uvRotate(sign(cos(hash(vec2(uvApo.z) * 4.) * d * 180.)) * d * iTime * timeMultiplier * (1. / uvApo.z * 120.) - d * 90.);
|
||||
// draw needles
|
||||
c = mix(c, pal(.85), stroke(sdfRect(uvrh + vec2(uvApo.z - (uvApo.z * .8), .0), uvApo.z * vec2(.4, .03)), uvApo.z * .01, 0.005, 1.));
|
||||
c = mix(c, pal(.9), fill(sdfRect(uvrm + vec2(uvApo.z - (uvApo.z * .65), .0), uvApo.z * vec2(.5, .002)), 0.005, 1.));
|
||||
c = mix(c, pal(.5 + r * 10.), fill(circle + uvApo.z - .02, 0.005, 1.)); // center
|
||||
// drawing the gears
|
||||
} else if (uvApo.z > .05) {
|
||||
vec2 uvrg = uvApo.xy * uvRotate(sign(cos(hash(vec2(uvApo.z + 2.)) * d * 180.)) * d * iTime * timeMultiplier * (1. / uvApo.z * 20.));
|
||||
float g = stroke(mod(atan(uvrg.y, uvrg.x) + d * 22.5, d * 45.) - d * 22.5, .3, .05, 1.0);
|
||||
vec2 size = uvApo.z * vec2(.45, .08);
|
||||
c = mix(c, pal(.55 - r * .6), fill(circle + g * (uvApo.z * .2) + .01, .001, 1.) * fill(circle + (uvApo.z * .6), .005, .0));
|
||||
c = mix(c, pal(.55 - r * .6), fill(min(sdfRect(uvrg, size.xy), sdfRect(uvrg, size.yx)), .005, 1.));
|
||||
// drawing the screws
|
||||
} else {
|
||||
vec2 size = uvApo.z * vec2(.5, .1);
|
||||
c = mix(c, pal(.85 - (uvApo.z * 2.)), fill(circle + 0.01, .007, 1.));
|
||||
c = mix(c, pal(.8 - (uvApo.z * 3.)), fill(min(sdfRect(uvApo.xy, size.xy), sdfRect(uvApo.xy, size.yx)), .002, 1.));
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
||||
vec2 uv = (fragCoord.xy - iResolution.xy * .5) / iResolution.y;
|
||||
vec4 ms = (iMouse - iResolution.xyxy * .5) / iResolution.y;
|
||||
vec4 col = vec4(scene(uv * 4., ms * 4.), 1.0);
|
||||
|
||||
vec2 termUV = fragCoord.xy / iResolution.xy;
|
||||
vec4 terminalColor = texture(iChannel0, termUV);
|
||||
|
||||
float alpha = step(length(terminalColor.rgb), BLACK_BLEND_THRESHOLD);
|
||||
vec3 blendedColor = mix(terminalColor.rgb * 1.0, col.rgb * 0.3, alpha);
|
||||
|
||||
fragColor = vec4(blendedColor, terminalColor.a);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue