// Frostline, a stand-in world from zaisma.com/developers/backgrounds. MIT licensed: free to start from. float h1(float n) { return fract(sin(n * 127.1) * 43758.5453); } float h2(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); } vec2 h22(vec2 p) { p = vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3))); return fract(sin(p) * 43758.5453); } float noise(vec2 p) { vec2 i = floor(p), f = fract(p); vec2 u = f * f * (3.0 - 2.0 * f); return mix(mix(h2(i), h2(i + vec2(1.0, 0.0)), u.x), mix(h2(i + vec2(0.0, 1.0)), h2(i + vec2(1.0, 1.0)), u.x), u.y); } float fbm(vec2 p) { float v = 0.0, a = 0.5; for (int i = 0; i < 5; i++) { v += a * noise(p); p = p * 2.03 + 1.7; a *= 0.5; } return v; } // Soft particles in a grid of cells: each cell holds at most one, at a random spot, drifting with the grid. float motes(vec2 p, float size, float chance, float seed) { vec2 id = floor(p), f = fract(p) - 0.5; vec2 r = h22(id + seed) - 0.5; float on = step(1.0 - chance, h2(id + seed * 1.3)); float d = length(f - r * 0.7); return on * smoothstep(size, 0.0, d); } // 1D value noise, and a ridged octave of it (sharp crests). float vn(float x) { float i = floor(x), f = fract(x); return mix(h1(i), h1(i + 1.0), f * f * (3.0 - 2.0 * f)); } float rdg(float x) { return 1.0 - abs(vn(x) * 2.0 - 1.0); } // Lopsided summits (the three nearest, plus a spire), crests broken by ridged noise. x: skyline, y: across the ridge of the // nearest summit covering y (positive on its sunward face), z: that face's steepness, w: the skyline upwind (spindrift). vec4 peaks(float x, float y, float seed, float spireX, float tall, float lean) { float c = floor(x); vec3 r = vec3(h1(c - 1.0 + seed), h1(c + seed), h1(c + 1.0 + seed)), r2 = fract(r * 13.3 + 0.37); vec3 xi = c + vec3(-1.0, 0.0, 1.0) + 0.15 + 0.7 * r, hi = 0.35 + 0.65 * r2; vec3 dx = x - xi, sl = mix(1.9 - 0.9 * r2, 1.0 + 0.9 * r, step(0.0, dx)), v = hi - abs(dx) * sl; float ds = x - spireX, vs = tall - abs(ds) * (ds < 0.0 ? 1.0 : 1.7); float jag = rdg(x * 3.1 + seed) * 0.14 + rdg(x * 7.3 + seed) * 0.06; float best = max(max(max(v.x, v.y), max(v.z, vs)), 0.0); vec3 key = fract(r * 31.7) + step(v, vec3(y)) * 9.0, side = dx + (hi - y) * (0.55 * r - 0.2); float k = min(key.x, key.y), pick = step(key.y, key.x), pz = step(key.z, k); float sd = mix(mix(side.x, side.y, pick), side.z, pz), st = mix(mix(sl.x, sl.y, pick), sl.z, pz); float onSpire = step(y, vs); sd = mix(sd, ds + (tall - y) * 0.18, onSpire); st = mix(st, 1.4, onSpire); float upwind = max(y - best + jag, 0.0) * lean; vec3 du = dx + upwind; vec3 vu = hi - abs(du) * mix(1.9 - 0.9 * r2, 1.0 + 0.9 * r, step(0.0, du)); float dsu = ds + upwind; return vec4(best - jag, sd, st, max(max(max(vu.x, vu.y), vu.z), tall - abs(dsu) * (dsu < 0.0 ? 1.0 : 1.7))); } // One range over col. glow: dusk light it catches, fog: how far it ghosts into the sky. vec3 range(vec3 col, vec2 p, float fi, float sc, float amp, float base, float fog, float tall, float glow, float fx, float sunX, vec3 sky, float px) { float y = (p.y - base) / amp; vec4 pk = peaks(p.x * sc + fi * 7.3, y, fi * 13.0 + 5.0, fx * sc + fi * 7.3, tall, amp * 1.3 * sc); float d = (pk.x - y) * amp, below = max(d, 0.0); float near = exp(-abs(p.x - sunX) * 0.7), alpen = smoothstep(base - 0.05, base + amp * 0.9, p.y) * near * glow; float lit = smoothstep(-0.02, 0.02, pk.y + (vn(y * 9.0 + fi * 5.0) - 0.5) * 0.07); // jagged ridge line vec3 shade = mix(vec3(0.09, 0.13, 0.27), vec3(0.2, 0.26, 0.46), smoothstep(0.25, 0.0, below)); vec3 sunlit = mix(vec3(0.38, 0.44, 0.64), vec3(1.0, 0.66, 0.52), alpen); vec3 face = mix(shade, sunlit, lit); // Bare rock streaks down the faces where snow can't hold. if (fi > 0.5) { float streak = noise(vec2(pk.y * 9.0 * sc, p.y * sc * 3.0 + fi * 3.0)) * (0.6 + 0.5 * vn(p.y * sc * 1.5 + pk.y * 3.0 * sc)); float rock = smoothstep(0.62 - pk.z * 0.08, 0.76 - pk.z * 0.08, streak) * smoothstep(0.0, 0.02, below); face *= 0.92 + 0.16 * streak; face = mix(face, mix(vec3(0.04, 0.05, 0.09), vec3(0.3, 0.2, 0.21), lit * (0.3 + alpen)), rock * 0.9); } face += vec3(1.0, 0.76, 0.62) * exp(-below * 260.0) * (0.1 + lit * alpen * 0.7); // Mist pools low; far ranges ghost into the sky. face = mix(face, mix(sky, vec3(0.3, 0.32, 0.5), 0.3), clamp(fog + smoothstep(0.04, 0.3, d) * (0.4 - fog * 0.2 - step(2.5, fi) * 0.15), 0.0, 1.0)); col = mix(col, face, smoothstep(-px, px, d)); if (fi > 0.5 && fi < 2.5) { // spindrift off the crests float above = max(-d, 0.0); float plume = smoothstep(0.5, 0.95, pk.w) * exp(-above * 12.0) * step(0.0, -d); plume *= smoothstep(0.2, 0.8, noise(vec2((p.x + iTime * 0.5) * 4.0 + above * 6.0, above * 30.0 - iTime * 0.7))); col = mix(col, mix(vec3(0.6, 0.66, 0.86), vec3(1.0, 0.78, 0.65), near), plume * 0.75); } return col; } // Flakes of varied size and brightness, one per cell at most; q is stretched along the wind. float flakes(vec2 q, float chance, float smin, float smax, float seed) { vec2 id = floor(q), r = h22(id + seed); float k = h2(id + seed + 7.3), s = mix(smin, smax, fract(k * 7.7)); return step(1.0 - chance, k) * smoothstep(s, s * 0.25, length(fract(q) - 0.5 - (r - 0.5) * 0.4)) * (0.5 + 0.5 * fract(k * 13.1)); } void mainImage(out vec4 color, in vec2 pixel) { vec2 uv = pixel / iResolution.xy; float ar = iResolution.x / iResolution.y; vec2 p = vec2(uv.x * ar, uv.y); float px = 1.5 / iResolution.y, t = iTime, wide = clamp((ar - 0.6) / 0.9, 0.0, 1.0), lift = (1.0 - wide) * 0.12; // Sky: indigo, mauve, the afterglow low on the right. vec2 sun = vec2(ar * mix(0.8, 0.84, wide), 0.3 + lift); float g = length((p - sun) * vec2(0.45, 1.5)); vec3 sky = mix(vec3(0.36, 0.32, 0.5), vec3(0.04, 0.06, 0.17), smoothstep(0.3 + lift, 1.0, uv.y)); sky += vec3(1.0, 0.45, 0.25) * exp(-g * 2.4) * 0.8 + vec3(1.0, 0.78, 0.5) * exp(-g * 7.0) * 0.7; vec3 col = sky; // Four ranges; the second carries the summit, right of centre. float fx = ar * mix(0.6, 0.66, wide); col = range(col, p, 0.0, 3.2, 0.2, 0.4 + lift, 0.7, 0.0, 1.0, fx, sun.x, sky, px); col = range(col, p, 1.0, 2.0, 0.26, 0.27 + lift, 0.34, 1.45, 1.0, fx, sun.x, sky, px); col = range(col, p, 2.0, 2.6, 0.22, 0.17 + lift * 0.7, 0.14, 0.0, 0.6, fx, sun.x, sky, px); col = range(col, p, 3.0, 3.4, 0.16, 0.04 + lift * 0.5, 0.0, 0.0, 0.35, fx, sun.x, sky, px); // Gust fronts sweep through, then ease. float front = smoothstep(0.35, 0.8, noise(vec2(p.x * 0.6 + p.y * 0.3 + t * 0.45, t * 0.07))); float stream = noise(vec2((p.x + t * 0.9) * 3.0, p.y * 18.0)); stream *= stream; col = mix(col, vec3(0.34, 0.38, 0.56), smoothstep(0.12, 0.5, stream) * (0.15 + 0.5 * front) * (1.0 - uv.y * 0.7)); // Snow: many small far flakes, gust streaks, big near ones, a few soft out-of-focus ones. vec2 wd = vec2(-0.96, -0.28), wn = vec2(0.28, -0.96); float gust = t * 1.3 + 0.8 * sin(t * 0.37) + 0.4 * sin(t * 0.9 + 1.0); vec3 sc = mix(vec3(0.62, 0.68, 0.88), vec3(1.0, 0.8, 0.68), exp(-g * 1.5)); vec2 q = vec2(dot(p, wd) * 0.45, dot(p, wn)) * 70.0 + vec2(-gust, 0.0); col = mix(col, sc, flakes(q, 0.3, 0.14, 0.2, 1.0) * (0.15 + 0.35 * front)); q = vec2(dot(p, wd) * 0.32, dot(p, wn)) * 36.0 + vec2(-gust * 1.6, sin(t * 0.7) * 0.3); col = mix(col, sc, flakes(q, 0.3, 0.09, 0.24, 2.0) * (0.2 + 0.6 * front)); q = vec2(dot(p, wd) * 0.12, dot(p, wn)) * 30.0 + vec2(-gust * 1.2, 0.0); col = mix(col, sc, flakes(q, 0.3, 0.07, 0.12, 5.0) * front * 0.6); q = vec2(dot(p, wd) * 0.3, dot(p, wn)) * 14.0 + vec2(-gust * 2.3, sin(t * 0.5 + 1.0) * 0.4); col = mix(col, sc, flakes(q, 0.2, 0.08, 0.22, 3.0) * (0.55 + 0.45 * front)); q = vec2(dot(p, wd) * 0.45, dot(p, wn)) * 5.0 + vec2(-gust * 3.2, 0.0); col = mix(col, sc * 1.05, smoothstep(0.0, 0.6, flakes(q, 0.14, 0.08, 0.18, 4.0)) * 0.28); color = vec4(col, 1.0); }