mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-30 14:58:08 +00:00
Nine concepts to the owner's mockup, grey on true black, the subject right
of centre and the left 40% kept dark for a card's words: Enso a brushed
ensÅ shedding particles over water and mist, Kai three panes of glass
threaded by falling light, Zen a sphere in raked sand, Iro mountains and
cloud spilling out of a frame, Koma film frames running into depth, Oto a
mirrored waveform, Koe twisting ribbons, Uta a fan rising over strings,
Mori a wireframe peak.
Each is one fragment shader over film/models/gl.js, a pure function of the
loop's phase, so the frame after the last is the first. film.mjs steps it
on the GPU, refuses a seam, and cuts public/models/<name>.{webp,webm,mp4}:
1200x1500 for every model and a 1500x1500 Enso, 30 fps, each loop under
700 KB. Frames stay in film/models/frames (ignored).
111 lines
4.4 KiB
HTML
111 lines
4.4 KiB
HTML
<!doctype html>
|
|
<html data-seconds="8">
|
|
<meta charset="utf-8" />
|
|
<title>Iro</title>
|
|
<style>html,body{margin:0;background:#000}canvas{display:block}</style>
|
|
<body>
|
|
<canvas></canvas>
|
|
<!--
|
|
Iro: a thin frame, and the picture in it will not stay inside. Ink-wash
|
|
mountains rise through its foot and run on past both edges, cloud billows
|
|
out of it to the right, and mist lies in the valleys between the ranges. In
|
|
the loop the clouds turn over and the mist flows through once.
|
|
-->
|
|
<script type="x-shader/x-fragment">
|
|
// The frame, in heights from the top-left: x0, y0, x1, y1.
|
|
vec4 frame(){
|
|
float w = R.x / R.y;
|
|
return vec4(w * .575, .2, w * .83, .61);
|
|
}
|
|
|
|
// A ridge line: sharp peaks over a softer fall.
|
|
float ridge(float x, float seed){
|
|
float r = 0., a = .5, fr = 1.;
|
|
for (int k = 0; k < 6; k++) {
|
|
float n = 1. - abs(gn(vec2(x * fr + seed, seed * 1.7)));
|
|
r += a * n * n;
|
|
fr *= 2.03; a *= .48;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
// A mountain's face at p under the ridge: rock and snow, lit from the upper right.
|
|
float rock(vec2 p, float depth, float seed){
|
|
vec2 q = vec2(p.x * 11., depth * 11.) + seed;
|
|
float e = .02;
|
|
float n = fbm(q);
|
|
float dx = (fbm(q + vec2(e, 0.)) - n) / e;
|
|
float gully = .5 + .5 * clamp(dx * .35, -1., 1.);
|
|
float snow = smoothstep(.0, .5, fbm(vec2(p.x * 9., depth * 4.) + seed * 2.) + .2 - depth * 3.);
|
|
return mix(.05, .42, gully * gully) * (.4 + .6 * snow);
|
|
}
|
|
|
|
// Cloud: billows turning over in place, with a bright crown.
|
|
float cloud(vec2 p, vec2 at, vec2 size){
|
|
vec2 q = (p - at) / size;
|
|
float r = length(q * vec2(1., 1.25));
|
|
vec2 w = vec2(pfbm(q * 1.6 + vec2(P * 2., 0.), 2., 4), pfbm(q * 1.6 + vec2(P * 2., 7.), 2., 4));
|
|
float n = pfbm(q * 2.2 + w * .9 + vec2(P * 4., 3.), 4., 6);
|
|
float body = smoothstep(.15, -.35, r - 1. + n * .9);
|
|
float crown = smoothstep(.3, .9, n + .4 - q.y * .3);
|
|
return body * (.35 + .65 * crown);
|
|
}
|
|
|
|
float scene(vec2 f){
|
|
vec2 p = f / R.y;
|
|
vec4 fr = frame();
|
|
float cx = (fr.x + fr.z) * .5;
|
|
float hw = (fr.z - fr.x) * .5;
|
|
float px = 1. / R.y;
|
|
vec2 dIn = vec2(max(fr.x - p.x, p.x - fr.z), max(fr.y - p.y, p.y - fr.w));
|
|
float outside = max(dIn.x, dIn.y);
|
|
float inside = 1. - smoothstep(-px, px, outside);
|
|
|
|
// Inside the frame, a pale sky of mist over the ranges; outside, the dark.
|
|
float L = inside * (.05 + .14 * smoothstep(fr.y, fr.w, p.y));
|
|
|
|
// Three ranges, far to near. Each one fades out the farther it runs from the frame.
|
|
for (int i = 0; i < 3; i++) {
|
|
float fi = float(i);
|
|
float base = mix(.5, .66, fi / 2.);
|
|
float span = mix(.9, 2.2, fi / 2.);
|
|
float top = base - .17 * ridge((p.x - cx) * (2.2 + fi * .6) + 1.3 + fi * 2.7, 4. + fi * 3.)
|
|
- (.13 - .03 * fi) * exp(-pow((p.x - cx - .04 + .05 * fi) / (.07 + .04 * fi), 2.));
|
|
if (p.y > top) {
|
|
float depth = p.y - top;
|
|
float face = rock(p, depth, fi * 5.);
|
|
// Far ranges are paler in the mist; every range washes out into mist at its foot.
|
|
float wash = smoothstep(.05 - .02 * fi, .26 - .06 * fi, depth);
|
|
float mist = mix(.3, .14, fi / 2.);
|
|
float ink = mix(face * (1.1 - .25 * fi), mist, wash * .85);
|
|
float rim = exp(-depth * R.y / 2.) * (.45 - .1 * fi);
|
|
float reach = inside + (1. - inside) * exp(-pow(max(abs(p.x - cx) - hw, 0.) / (.1 * span), 2.)) * smoothstep(fr.w + .12, top - .02, p.y - .1 * fi);
|
|
reach *= 1. - smoothstep(.64, .8, p.y);
|
|
L = mix(L, ink + rim, reach);
|
|
}
|
|
}
|
|
|
|
// Mist lying in the valleys, flowing through once per loop.
|
|
float m = pfbm(vec2(p.x * 2.5 + P * 2., p.y * 10.), 2., 5) * .5 + .5;
|
|
float vale = exp(-pow((p.y - .585) / .03, 2.)) + .7 * exp(-pow((p.y - .675) / .035, 2.));
|
|
float spread = exp(-pow(max(abs(p.x - cx) - hw, 0.) / .2, 2.));
|
|
L += smoothstep(.45, .9, m) * vale * spread * .5 * (1. - smoothstep(.66, .78, p.y));
|
|
|
|
// Cloud billowing out of the frame to the right, and a smaller one over its foot to the left.
|
|
float cl = cloud(p, vec2(fr.z - .01, .5), vec2(.13, .08)) + .7 * cloud(p, vec2(fr.x + .04, .585), vec2(.08, .04));
|
|
L = mix(L, 1.25, clamp(cl, 0., 1.) * .88);
|
|
|
|
// The frame line, fine and bright, lost where the cloud passes in front of it.
|
|
float dEdge = abs(outside);
|
|
float rim = line(dEdge / px, .7) * (1. - clamp(cl, 0., 1.) * .9);
|
|
L += rim * 1.3 + exp(-dEdge / (px * 8.)) * .12 * (1. - clamp(cl, 0., 1.));
|
|
|
|
// The left of the frame is the words': keep it dark.
|
|
L *= mix(.1, 1., smoothstep(.24, .5, f.x / R.x));
|
|
return tone(L * 1.6);
|
|
}
|
|
</script>
|
|
<script src="./gl.js"></script>
|
|
</body>
|
|
</html>
|