// Emberfall, 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 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); } float fb(vec2 p) { return vn(p) * 0.55 + vn(p * 2.1 + 3.0) * 0.3 + vn(p * 4.3 + 7.0) * 0.15; } // Crust plates of uneven sizes (weighted cells): x = distance to the crack, y = the plate's value. vec2 plates(vec2 g) { vec2 i = floor(g), f = fract(g); float d1 = 8.0, d2 = 8.0, id = 0.0; for (int y = -1; y <= 1; y++) for (int x = -1; x <= 1; x++) { vec2 o = vec2(float(x), float(y)); float h = hash2(i + o); vec2 r = o + 0.5 + 0.8 * (vec2(h, fract(h * 13.7)) - 0.5) - f; float d = length(r) * (0.65 + 0.7 * fract(h * 31.3)); if (d < d1) { d2 = d1; d1 = d; id = h; } else if (d < d2) d2 = d; } return vec2(d2 - d1, id); } 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; // The volcano: right of centre on wide, high on portrait. float wide = smoothstep(0.8, 1.4, ar); float hor = mix(0.5, 0.4, wide), vx = ar * mix(0.56, 0.69, wide), top = hor + mix(0.22, 0.3, wide); vec3 col; if (uv.y > hor) { float y = uv.y - hor; p.x += sin(uv.y * 30.0 + t * 0.7) * 0.002 * exp(-y * 14.0); // heat haze, slow and soft vec2 q = vec2(p.x - vx, uv.y - top); // Sky: smoke banks, red where the fire lights them. float s = fb(vec2(p.x * 1.6 - t * 0.015, y * 3.5 / (y + 0.4) - t * 0.01)); col = mix(vec3(0.6, 0.1, 0.02), vec3(0.03, 0.015, 0.014), smoothstep(0.0, 0.45, y)); col *= 0.15 + 1.6 * smoothstep(0.25, 0.75, s); col += vec3(0.8, 0.14, 0.02) * exp(-length(q * vec2(0.9, 0.6)) * 3.0) * 0.45; // The ash cloud: dark billows rolling up, swelling and leaning downwind, lit from beneath. float up = max(q.y, 0.0), xx = q.x + up * up * 0.9 + up * 0.1, w = 0.05 + up * 0.7 + up * up * 0.5; vec2 aq = vec2(xx, q.y - t * 0.03) * 3.2 / (0.22 + up); aq.x += sin(aq.y * 0.9 + t * 0.15) * 0.35; // edges rolling over float b1 = fb(aq), b2 = fb(aq + vec2(0.1, -0.35)); float cloud = smoothstep(0.0, 0.16, 1.0 - abs(xx) / w + (b1 - 0.5) * (0.5 + 1.8 * min(up, 0.35)) + 0.3 * smoothstep(0.2, 0.0, up)) * smoothstep(-0.01, 0.02, q.y); float lit = clamp((b1 - b2) * 3.0 + 0.45, 0.0, 1.0), fire = exp(-up * 4.5); vec3 ash = vec3(0.045, 0.028, 0.025) * (0.6 + 0.8 * b1); // dark body, deeper in the folds ash += vec3(0.55, 0.11, 0.025) * lit * lit * (0.25 + 0.75 * exp(-up * 1.6)); // undersides glowing red ash += vec3(1.0, 0.45, 0.1) * lit * fire; // and burning over the crater col = mix(col, ash, cloud * 0.97); col += vec3(1.0, 0.55, 0.15) * exp(-abs(q.x) * 28.0 / (1.0 + up * 6.0)) * smoothstep(0.1, 0.0, q.y) * exp(-max(-q.y, 0.0) * 60.0) * (0.75 + 0.25 * vn(vec2(q.x * 15.0, q.y * 8.0 - t * 1.2))); // fire fountain // The cone: massive, rugged, a shoulder, a broken crater rim; the far shore behind. float rough = vn(vec2(p.x * 6.0, 4.0)) * 0.5 + vn(vec2(p.x * 17.0, 5.0)) * 0.32 + vn(vec2(p.x * 45.0, 6.0)) * 0.18; float ax = min(abs(q.x), 0.62), coneH = top - ax * (q.x > 0.0 ? 1.15 : 0.95) + ax * ax * 0.8 - max(abs(q.x) - 0.62, 0.0); coneH = max(coneH, top - 0.14 - (q.x + 0.23) * (q.x + 0.23) * 3.5); // the shoulder coneH = min(coneH, top + 0.012 * (vn(vec2(p.x * 55.0, 2.0)) - 0.5) - 0.028 * smoothstep(0.04, 0.0, abs(q.x + 0.006))); coneH += (rough - 0.5) * 0.09 * smoothstep(0.0, 0.06, abs(q.x + 0.006)); // crags, sparing the crater float cone = max(coneH, hor + 0.02 + 0.03 * vn(vec2(p.x * 6.0, 9.0)) + 0.035 * smoothstep(0.5, 0.9, vn(vec2(p.x * 4.0, 6.0))) * (1.0 + vn(vec2(p.x * 16.0, 7.0)))); float dh = cone - uv.y; if (dh > -px) { // Lava channels meandering down, splitting, pulsing slowly. float v = top - uv.y; float u = q.x / (v + 0.04) + 0.7 * (vn(vec2(v * 4.0, 1.3)) - 0.5) + 0.35 * (vn(vec2(v * 11.0, q.x * 5.0)) - 0.5); float ua = u * 3.2 + 0.3, ub = u * 3.2 + 0.8 + (vn(vec2(v * 6.0, floor(u * 3.2 + 0.8))) - 0.5) * 1.6 * smoothstep(0.05, 0.2, v); float wd = 0.9 - v * 0.3, aa = px * 9.0 / (v + 0.04) + 0.01; float sa = (1.0 - abs(fract(ua) - 0.5) * 2.0) * step(0.3, hash2(vec2(floor(ua), 3.0))); float sb = (1.0 - abs(fract(ub) - 0.5) * 2.0) * step(0.45, hash2(vec2(floor(ub), 5.0))) * smoothstep(0.06, 0.12, v); float sm = max(sa, sb), ca = smoothstep(wd - aa, wd + aa, sm), cb = smoothstep(wd + 0.04 - aa, wd + 0.06 + aa, sm); float pulse = (0.65 + 0.35 * sin(t * 0.5 + floor(ua) * 2.3)) * (0.7 + 0.5 * vn(vec2(u * 4.0, v * 9.0 - t * 0.35))); float on = smoothstep(0.0, 0.025, coneH - uv.y) * smoothstep(0.015, 0.05, v); vec3 flank = vec3(0.032, 0.019, 0.017) * (0.5 + 0.6 * rough + 0.6 * vn(vec2(u * 7.0, v * 4.0))); // gullies down the flanks flank += (vec3(0.9, 0.2, 0.02) * ca + vec3(0.5, 0.45, 0.2) * cb + vec3(0.6, 0.1, 0.01) * smoothstep(wd - 0.2, wd, sm) * 0.35) * pulse * on; // glow, core, halo flank += vec3(0.9, 0.25, 0.04) * exp(-(uv.y - hor) * 25.0) * 0.45; // the field's glow on the foot flank += vec3(1.0, 0.38, 0.07) * exp(-v * 7.0) * smoothstep(0.32, 0.0, abs(q.x)) * (0.3 + 0.4 * rough); // the crater lights the upper flanks flank += vec3(1.0, 0.5, 0.1) * exp(-dh * 70.0) * smoothstep(0.08, 0.0, abs(q.x)) * 0.9; // the crater lip flank += vec3(0.9, 0.25, 0.04) * exp(-dh * 90.0) * 0.3; // rim lit by the fire col = mix(col, flank, smoothstep(-px, px, dh)); } } else { // The lava field in perspective. float z = 1.0 / (hor - uv.y + 0.025), far = smoothstep(4.0, 22.0, z), pz = px * z * z; vec2 g = vec2((p.x - vx) * z * 0.6, z * 1.1); // The river winds out of the volcano toward us. float heat = fb(g * 0.3 + vec2(t * 0.02, 0.0)); float rc = sin(z * 0.3 + 0.8) * 1.6 + sin(z * 0.75) * 0.5, rw = 0.5 + 0.15 * heat; float rd = abs(g.x - rc) - rw; // Heat: by the river, under the volcano, in patches. float H = clamp(max(exp(-max(rd, 0.0) * 0.8), exp(-abs(p.x - vx) * 2.5 - (hor - uv.y) * 7.0)) * 0.85 + (heat - 0.5) * 0.7, 0.0, 1.0); float warp = vn(g * 0.6); vec2 gw = g + vec2(warp, vn(g * 0.6 + 5.0)) * 1.4 + vec2(vn(g * 2.7), vn(g * 2.7 + 9.0)) * 0.3; // crooked plate edges vec2 pl = plates(gw); // Cracks: hairline and deep red where cool, wide and white-hot where not. float cw = mix(0.004, 0.11, H * H) * (0.6 + 0.8 * vn(gw * 2.0)); float seam = smoothstep(cw + pz, max(cw - pz, 0.0) * 0.5, pl.x) * min(1.0, cw / pz + 0.15); float sunk = smoothstep(0.85 - 0.45 * H, 0.88 - 0.45 * H, pl.y) * smoothstep(0.32, 0.12, pl.x) * (0.4 + 0.5 * H); // plates half-sunk in glow vec3 molten = mix(vec3(0.42, 0.035, 0.01), vec3(1.0, 0.42, 0.05), smoothstep(0.1, 0.7, H)) + vec3(0.6, 0.5, 0.3) * smoothstep(cw * 0.6, 0.0, pl.x) * H * H * H; // The crust: rough, a faint red sheen, glowing where thin by the cracks. float tex = vn(gw * 4.0 + pl.y * 20.0) * (1.0 - far); vec3 crust = vec3(0.03, 0.019, 0.017) * (0.55 + 0.6 * pl.y) * (0.65 + 0.7 * tex); crust += vec3(0.32, 0.05, 0.02) * (0.25 + 0.75 * smoothstep(0.4, 0.85, vn(g * 1.1 + 7.0))) * (0.12 + 0.2 * exp(-abs(p.x - vx) * 2.0)) * (1.0 - far) * (0.5 + 0.8 * tex); // red sheen crust += vec3(0.8, 0.12, 0.01) * smoothstep(0.25, 0.0, pl.x) * H * 0.3; vec3 ground = mix(crust, molten, mix(max(seam, sunk), 0.2 + 0.35 * H, far)); // The river flowing: bright core, hot streaks, floes of crust drifting toward us. float river = smoothstep(pz + 0.02, -0.02, rd), sx = (g.x - rc) / rw, fl = g.y + t * 0.45; float core = clamp(1.0 - sx * sx, 0.0, 1.0); float streak = vn(vec2(sx * 2.5, fl * 0.7)); vec3 flow = mix(vec3(0.8, 0.13, 0.02), vec3(1.0, 0.6, 0.14), core) + vec3(0.6, 0.5, 0.3) * smoothstep(0.45, 0.85, streak) * core; float fn = vn(vec2(g.x * 2.2, fl * 1.5) + 3.0); float floe = smoothstep(0.6, 0.68, fn) * (1.0 - far); flow = mix(flow + vec3(0.4, 0.15, 0.0) * smoothstep(0.5, 0.6, fn) * (1.0 - floe), vec3(0.06, 0.03, 0.025), floe * 0.9); ground = mix(ground, flow, river); ground += vec3(0.9, 0.2, 0.02) * exp(-max(rd, 0.0) * 2.0) * 0.2; // the river's glow on the crust col = mix(ground, vec3(0.6, 0.12, 0.02), far * 0.5); } // Sparks rising on the heat. for (int i = 0; i < 2; i++) { float fi = float(i), sc = 8.0 + fi * 7.0; vec2 a = vec2(p.x * sc + t * 0.15 + sin(p.y * 4.0 + t * 0.7 + fi) * 0.5, p.y * sc - t * (1.0 + fi * 0.5)); col += vec3(1.0, 0.62, 0.22) * motes(a, 0.11 - fi * 0.03, 0.22, fi * 4.0) * (1.0 - fi * 0.3); } col = col / (1.0 + col * 0.3); vec2 vg = (uv - vec2(0.6, 0.45)) * vec2(0.9, 1.2); color = vec4(col * (1.0 - 0.5 * dot(vg, vg)), 1.0); }