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).
94 lines
3.4 KiB
HTML
94 lines
3.4 KiB
HTML
<!doctype html>
|
|
<html data-seconds="6">
|
|
<meta charset="utf-8" />
|
|
<title>Koma</title>
|
|
<style>html,body{margin:0;background:#000}canvas{display:block}</style>
|
|
<body>
|
|
<canvas></canvas>
|
|
<!--
|
|
Koma: a row of film frames standing one behind another and running away into
|
|
the dark, each holding the same range of mountains a moment further on. The
|
|
near frames are lit, the far ones fade. In the loop the row slides forward
|
|
by one frame: the nearest leaves past the edge, a new one arrives at the far
|
|
end, and the mountains pan as they come.
|
|
-->
|
|
<script type="x-shader/x-fragment">
|
|
const int N = 9;
|
|
|
|
// A range of peaks along x: one peak to a cell, each its own height and
|
|
// width, roughened by a little ridged noise. Endless, so a frame can pan.
|
|
float range(float x, float seed){
|
|
float top = 0.;
|
|
float c = floor(x);
|
|
for (int k = -2; k <= 2; k++) {
|
|
float id = c + float(k);
|
|
vec2 h = h22(vec2(id, seed));
|
|
float at = id + h.x;
|
|
float hgt = .35 + .65 * h.y;
|
|
float w = .55 + .5 * h11(id * 3.1 + seed);
|
|
float t = max(1. - abs(x - at) / w, 0.);
|
|
top = max(top, hgt * pow(t, 1.5) * (.8 + .2 * t));
|
|
}
|
|
float n = 0., a = .5, fr = 3.;
|
|
for (int k = 0; k < 4; k++) { n += a * (1. - abs(gn(vec2(x * fr, seed)))); fr *= 2.1; a *= .5; }
|
|
return top + n * .2 * (.5 + top);
|
|
}
|
|
|
|
// What a frame holds at (u, v), u and v in -1..1, v down; `pan` is how far on it is.
|
|
float shot(vec2 uv, float pan){
|
|
float x = uv.x * 1.1 + pan;
|
|
// A sky lit from behind the ranges, brightest at the horizon.
|
|
float L = .04 + .7 * exp(-pow((uv.y - .12) / .42, 2.));
|
|
// The far range, pale in the haze.
|
|
float far = .2 - .62 * range(x * .9 + 3., 2.);
|
|
if (uv.y > far) L = mix(.34, .14, smoothstep(far, far + .4, uv.y)) + exp(-(uv.y - far) * 160.) * .3;
|
|
// The near range, dark, its ridge caught by the light behind it.
|
|
float near = .42 - .55 * range(x * 1.4 + 7., 9.);
|
|
if (uv.y > near) {
|
|
float d = uv.y - near;
|
|
L = .02 + .035 * (1. - smoothstep(.0, .4, d)) + exp(-d * 130.) * .75;
|
|
}
|
|
// Mist lying in the valley, thin.
|
|
float m = smoothstep(.1, .8, gn(vec2(x * 2., uv.y * 5.)) * .5 + .5);
|
|
L += m * exp(-pow((uv.y - .5) / .07, 2.)) * .14;
|
|
return L;
|
|
}
|
|
|
|
float scene(vec2 f){
|
|
vec2 c = vec2(.57 * R.x, .5 * R.y);
|
|
vec2 p = (f - c) / R.y / 1.38;
|
|
float px = 1. / R.y / 1.38;
|
|
float L = 0.;
|
|
// Far to near, each frame over the ones behind it.
|
|
for (int i = N; i >= 0; i--) {
|
|
float s = float(i) - P;
|
|
float z = 1.85 + s * .5;
|
|
float x = .37 - s * .078;
|
|
vec2 hs = vec2(.18, .3) / z;
|
|
vec2 at = vec2(x / z, 0.);
|
|
vec2 uv = (p - at) / hs;
|
|
// A frame's light: arriving far away, lit near, gone past the edge.
|
|
float come = smoothstep(float(N), float(N) - 1.6, s);
|
|
float go = smoothstep(-1., -.25, s);
|
|
float lit = exp(-s * .17) * come * go;
|
|
vec2 e = (abs(p - at) - hs) / px;
|
|
float edge = max(e.x, e.y);
|
|
if (edge < 1.5) {
|
|
float inside = 1. - smoothstep(-.75, .75, edge);
|
|
float pic = shot(uv, s * .55) * lit;
|
|
// The picture is a little clear, so the frames behind show through it.
|
|
L = mix(L, pic, inside * (.72 + .2 * lit));
|
|
}
|
|
float rim = line(edge, .55 + .5 / z);
|
|
L += rim * (.55 + .9 * lit) * lit + exp(-abs(edge) / (5. + 6. / z)) * .1 * lit;
|
|
}
|
|
// A soft light from the near end, falling off into the depth.
|
|
// The left of the frame is the words': keep it dark.
|
|
L *= mix(.1, 1., smoothstep(.24, .5, f.x / R.x));
|
|
return pow(tone(L * 1.3), .95);
|
|
}
|
|
</script>
|
|
<script src="./gl.js"></script>
|
|
</body>
|
|
</html>
|