// Ashen Crown, 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); } float box(vec2 p, vec2 b) { vec2 d = abs(p) - b; return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0); } // Hash and value noise without sin. float hash(float p) { p = fract(p * 0.1031); p *= p + 33.33; return fract(p * (p + p)); } float hash2(vec2 p) { vec3 q = fract(p.xyx * 0.1031); q += dot(q, q.yzx + 33.33); return fract((q.x + q.y) * q.z); } float vn(vec2 p) { vec2 i = floor(p), f = fract(p), e = vec2(1.0, 0.0); f *= f * (3.0 - 2.0 * f); return mix(mix(hash2(i), hash2(i + e), f.x), mix(hash2(i + e.yx), hash2(i + 1.0), f.x), f.y); } // A tower: half width w, wall height h, spire height s. float tower(vec2 q, float w, float h, float s) { vec2 r = vec2(abs(q.x), q.y - h - s); return min(box(q - vec2(0.0, h * 0.5), vec2(w, h * 0.5)), max(r.x + r.y * (w < 0.03 ? w * 1.2 : w) / s, h - q.y)); } // The citadel as a distance (q: 0 at its gate). float citadel(vec2 q) { float d = box(q - vec2(-0.015, -0.11), vec2(0.17 - min(q.y, 0.0) * 0.3, 0.17)); // walls battered down the rock d = min(d, max(box(q - vec2(-0.015, 0.065), vec2(0.17, 0.006)), (0.25 - abs(fract(q.x * 55.0) - 0.5)) / 55.0)); // crenels d = min(d, tower(q - vec2(-0.01, 0.0), 0.06, 0.1, 0.1)); // great hall d = min(d, tower(q - vec2(0.025, 0.0), 0.019, 0.29, 0.17)); // the great spire d = min(d, tower(q - vec2(-0.062, 0.0), 0.015, 0.21, 0.11)); d = min(d, tower(q - vec2(0.088, 0.0), 0.014, 0.16, 0.08)); d = min(d, tower(q - vec2(-0.15, 0.0), 0.012, 0.12, 0.06)); return min(d, max(box(q - vec2(0.15, 0.07), vec2(0.016, 0.07)), q.y - 0.11 - (q.x - 0.15) * 1.6)); // broken tower } // Mountains: a triangular peak per cell, random spot, height and slope. float peaks(float x, float seed) { float c = floor(x), h = 0.0; for (int j = 0; j < 2; j++) { float id = c + (j == 0 ? 0.0 : sign(fract(x) - 0.5)), a = hash(id + seed * 13.0), b = hash(id * 1.7 + seed * 31.0); h = max(h, 0.35 + 0.65 * b * b - abs(x - id - 0.15 - 0.7 * a) * (1.1 + 1.2 * fract(a * 7.0))); } return h; } // Sky without clouds: violet, magenta, crimson, amber horizon hottest under the sun. vec3 skyAt(vec2 p, vec2 sun, float hor) { float y = p.y - hor; vec3 c = mix(vec3(1.0, 0.5, 0.2), vec3(0.72, 0.15, 0.17), smoothstep(-0.02, 0.22 + 0.1 * exp(-abs(p.x - sun.x) * 1.2), y)); c = mix(c, vec3(0.34, 0.07, 0.26), smoothstep(0.15, 0.45, y)); c = mix(c, vec3(0.08, 0.025, 0.16), smoothstep(0.35, 0.75, y)); return c + vec3(1.0, 0.42, 0.14) * exp(-length(p - sun) * 4.0) * 0.75; } 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; // Focal point: right of centre on wide, upper half on portrait. float wide = smoothstep(0.8, 1.4, ar), cs = mix(0.78, 0.95, wide), cx = ar * mix(0.56, 0.68, wide), base = mix(0.55, 0.37, wide), hor = base - 0.02; vec2 sun = vec2(cx + 0.015 * cs, base + 0.12 * cs); float sr = 0.17 * cs; vec2 d = p - sun; float r = length(d), near = exp(-abs(d.x) * 1.1); float fog = vn(vec2(p.x * 2.2 - t * 0.04, uv.y * 8.0)); vec3 haze = skyAt(vec2(p.x, hor + 0.03), sun, hor); // The citadel, black against the sun, two windows still burning. vec2 q = (p - vec2(cx, base)) / cs; float fort = 0.0, win = 0.0, man = 0.0, cq = q.x, wq = p.x - cx + 0.46 * cs; if (abs(q.x) < 0.29 && q.y < 0.5 && q.y > -0.3) { fort = smoothstep(px, -px, citadel(q) * cs); win = smoothstep(0.003, 0.0, min(box(q - vec2(0.025, 0.14), vec2(0.0035, 0.01)), box(q - vec2(-0.02, 0.05), vec2(0.005, 0.009)))); } // The wanderer: hooded, cloaked, with a staff, his cloak stirring. vec2 w = vec2(wq, p.y - 0.19) / cs; if (abs(w.x) < 0.05 && w.y < 0.1) { float fd = max(box(w - vec2(sin(t * 1.4 + w.y * 40.0) * 0.003 * (w.y * 16.0 - 1.0), 0.025), vec2(0.008 + (0.05 - w.y) * 0.16, 0.025)), -w.y); man = smoothstep(px, -px, min(min(fd, length(w - vec2(0.002, 0.056)) - 0.008), box(w - vec2(0.016, 0.04), vec2(0.0013, 0.04))) * cs); } // Ridges nearest first, each covering its share of what's still open; then the sky. vec3 col = vec3(0.0); float rem = 1.0, air = 0.0; for (int i = 0; i < 5; i++) { float fi = float(i), k = fi / 4.0; float x = p.x * mix(2.2, 6.0, k) + fi * 7.3; float h = mix(0.0, hor - 0.03, k) + mix(0.13, 0.09, k) * (peaks(x, fi) + 0.3 * peaks(x * 2.7 + 5.0, fi + 2.0)); // peaks on peaks if (i == 0) h = max(h, 0.19 - 5.0 * wq * wq); // the wanderer's knoll if (i == 2) h = max(h, base + cs * (max(max(-0.11 - 1.3 * abs(cq + 0.13), -0.05 - 1.25 * abs(cq - 0.01)), -0.13 - 1.1 * abs(cq - 0.17)) + 0.04 * peaks(p.x * 20.0, 4.0))); float dh = h - uv.y, cover = smoothstep(-px, px, dh); float mist = smoothstep(0.0, 0.2, dh) * (1.0 - 2.0 * abs(fract(fog + fi * 0.33) - 0.5)); vec3 rock = mix(mix(vec3(0.04, 0.015, 0.07), haze, 0.03 + 0.8 * k * k), mix(haze, vec3(0.9, 0.32, 0.3), 0.3), mist * (0.12 + 0.55 * k)); float rim = clamp(1.0 - dh * 60.0, 0.0, 1.0) * near; rock += vec3(1.0, 0.55, 0.25) * rim * rim * (0.75 - 0.5 * k); // rim light if (i == 2) { rock = mix(rock, vec3(0.04, 0.015, 0.06), smoothstep(0.37, 0.12, abs(cq)) * (1.0 - mist * 0.6)); // the crag, dark against the sun rock = mix(rock, vec3(0.035, 0.013, 0.055) + vec3(1.0, 0.55, 0.18) * win, fort * (1.0 - cover)); cover = max(cover, fort); } if (i == 0) { rock = mix(rock, vec3(0.02, 0.008, 0.03), man * (1.0 - cover)); cover = max(cover, man); } col += rem * cover * rock; if (i >= 2) air += rem * (1.0 - cover) * 0.3; rem *= 1.0 - cover; if (rem < 0.002) break; } if (rem > 0.002) { vec3 sky = skyAt(p, sun, hor); // Clouds on a ceiling receding to the horizon, burning on the side facing the sun. float depth = 1.0 / (uv.y - hor + 0.1); vec2 kq = vec2((p.x - sun.x) * depth * 1.3 + t * 0.012, depth * 2.2 - t * 0.01), sq = kq + vec2(-d.x * 0.6, 0.35); float c1 = vn(kq) * 0.55 + vn(kq * 2.3 + 5.0) * 0.3 + vn(kq * 5.1 - 2.0) * 0.15; float c2 = vn(sq) * 0.6 + vn(sq * 2.3 + 5.0) * 0.4; float dens = smoothstep(0.4, 0.66, c1) * smoothstep(hor + 0.06, hor + 0.3, uv.y); float lit = clamp((c1 - c2) * 3.5 + 0.3, 0.0, 1.0); sky = mix(sky, mix(vec3(0.09, 0.03, 0.13), mix(vec3(0.85, 0.2, 0.2), vec3(1.0, 0.62, 0.3), exp(-r * 2.2)), lit * (0.2 + 0.8 * exp(-r * 1.3))), dens * 0.92); // The dying sun: swollen, limb-darkened, veiled, haloed. float disc = smoothstep(sr + px, sr - px, r); sky = mix(sky, mix(vec3(1.4, 1.1, 0.7), vec3(1.0, 0.3, 0.08), r * r / (sr * sr)), disc * (1.0 - dens * 0.55)); sky += vec3(1.0, 0.5, 0.2) * exp(-max(r - sr, 0.0) * 16.0) * 0.45 * (1.0 - disc); col += rem * sky; } // Shafts of light through the air over the far ridges. float shaft = vn(d / max(r, 1e-4) * 5.0 + t * 0.02); col += vec3(1.0, 0.5, 0.22) * smoothstep(0.35, 0.8, shaft) * exp(-r * 2.6) * smoothstep(sr, sr * 1.8, r) * (rem * 0.06 + air); // Embers rising, grey ash falling. vec2 a = vec2(p.x * 8.0 - t * 0.4 + sin(p.y * 3.0 + t * 0.35) * 0.6, p.y * 8.0 - t * 0.5); col += vec3(1.0, 0.48, 0.14) * motes(a, 0.12, 0.4, 0.0) * (0.7 + 0.3 * sin(t * 1.6 + a.y - a.x)); col += vec3(0.6, 0.5, 0.55) * motes(vec2(p.x * 11.0 - t * 0.5 + sin(p.y * 4.0 + t * 0.5) * 0.4, p.y * 11.0 + t * 0.4), 0.1, 0.3, 5.0) * 0.3; // Grade: soft shoulder, edges sinking into violet. col = col / (1.0 + col * 0.28); vec2 vg = (uv - vec2(0.62, 0.5)) * vec2(0.9, 1.2); color = vec4(mix(col, col * vec3(0.45, 0.32, 0.62), clamp(dot(vg, vg) * 0.9, 0.0, 1.0)), 1.0); }