fork a shader
Write or paste an ISF shader (GLSL ES with a /*{ ... }*/ header). It compiles + renders in yer browser. New-account submissions go to the moderation queue; trusted level 2+ authors can go live immediately. Everything is CC0. Got a file first? preview it locally →
A model marks this as witnessed_generation (honest AI provenance). Leave blank if you hand-authored it.
/*{ "DESCRIPTION": "Raymarched succulent garden: echeveria rosettes and jelly-bean sedum on a gravel bed, dew, golden-hour sun, slow orbit (loops every 40s at orbitSpeed 1). Drag sunPos to move the sun; camera, lens, leaf shape, blush, dew and haze are all live.", "INPUTS": [ { "NAME": "orbitSpeed", "TYPE": "float", "DEFAULT": 1.0, "MIN": 0.0, "MAX": 3.0 }, { "NAME": "camHeight", "TYPE": "float", "DEFAULT": 1.9, "MIN": 0.5, "MAX": 4.0 }, { "NAME": "camDist", "TYPE": "float", "DEFAULT": 2.5, "MIN": 1.2, "MAX": 6.0 }, { "NAME": "camAim", "TYPE": "float", "DEFAULT": 0.35, "MIN": -0.4, "MAX": 1.5 }, { "NAME": "lens", "TYPE": "float", "DEFAULT": 1.5, "MIN": 0.8, "MAX": 3.5 }, { "NAME": "sunPos", "TYPE": "point2D", "DEFAULT": [0.917, 0.29] }, { "NAME": "leafWidth", "TYPE": "float", "DEFAULT": 1.0, "MIN": 0.5, "MAX": 1.35 }, { "NAME": "leafCurl", "TYPE": "float", "DEFAULT": 1.0, "MIN": 0.0, "MAX": 1.5 }, { "NAME": "blush", "TYPE": "float", "DEFAULT": 1.0, "MIN": 0.0, "MAX": 2.0 }, { "NAME": "blushColor", "TYPE": "color", "DEFAULT": [0.85, 0.22, 0.30, 1.0] }, { "NAME": "dewAmount", "TYPE": "float", "DEFAULT": 1.0, "MIN": 0.0, "MAX": 2.0 }, { "NAME": "haze", "TYPE": "float", "DEFAULT": 1.0, "MIN": 0.0, "MAX": 4.0 } ] }*/ #define PI 3.14159265 #define PERIOD 40.0 #define CELL 1.7 // ---------- helpers ---------- mat2 rot(float a){ float c=cos(a), s=sin(a); return mat2(c,-s,s,c); } float hash21(vec2 p){ p = fract(p*vec2(123.34, 456.21)); p += dot(p, p+45.32); return fract(p.x*p.y); } vec2 hash22(vec2 p){ float h = hash21(p); return vec2(h, hash21(p + h + 17.3)); } float noise2(vec2 p){ vec2 i = floor(p), f = fract(p); f = f*f*(3.0-2.0*f); return mix(mix(hash21(i), hash21(i+vec2(1,0)), f.x), mix(hash21(i+vec2(0,1)), hash21(i+vec2(1,1)), f.x), f.y); } // material info written by map() for the closest surface float gLeafU; // 0 = leaf base, 1 = tip float gMat; // 0 ground, 1 rosette leaf, 2 bud, 3 sedum bead vec3 gCell; // per-plant random (x,y,z in 0..1) float gReal; // true distance to geometry in this cell (no neighbour bounds) — for shadow penumbra // one leaf in its own frame: grows along +x from 0..L, thickness T, half-width W float sdLeaf(vec3 p, float L, float W, float T){ float u = clamp(p.x/L, 0.0, 1.0); p.y -= 0.42*leafCurl*p.x*p.x/L; // curl upward p.y -= 0.16*p.z*p.z; // gently cupped cross-section float w = max(W * leafWidth * 1.35 * sqrt(u + 0.04) * (1.0 - u*u), 0.012); // widest past middle, pointed tip float t = max(T * (0.55 + 1.8*u*(1.0 - u)) * (1.0 - 0.45*u), 0.01); vec2 q = vec2(p.y/t, p.z/w); float d2 = (length(q) - 1.0) * min(t, w); float dx = max(-p.x, p.x - L); float d = length(max(vec2(d2, dx), 0.0)) + min(max(d2, dx), 0.0); gLeafU = u; return d; } // a ring of N leaves via polar repetition float sdRing(float r, float ang, float py, float N, float off, float tilt, float L, float W, float T, float lift){ float sec = 2.0*PI/N; float a = mod(ang + off, sec) - 0.5*sec; vec3 q = vec3(r*cos(a) + 0.12, py - lift, r*sin(a)); // leaves start tucked inside the centre q.xy *= rot(-tilt); return sdLeaf(q, L, W, T); } // a whole rosette; h = per-plant random params float sdRosette(vec3 p, vec3 h){ float sp = h.x; // species blend 0..1 : flat/broad -> tall/narrow float wide = mix(0.36, 0.20, sp); float thick = mix(0.13, 0.085, sp); float tiltBias = mix(0.0, 0.42, sp); float n0 = 12.0 + floor(h.y*5.0); // 12..16 outer leaves float best = 1e5; float bu = 0.0; float r = length(p.xz); float ang = atan(p.z, p.x); float d; d = sdRing(r, ang, p.y, n0, 0.0, 0.20+tiltBias, 1.00, wide, thick, 0.00); if (d<best){best=d; bu=gLeafU;} d = sdRing(r, ang, p.y, n0-4.0, 2.39996, 0.58+tiltBias, 0.74, wide*0.85, thick, 0.06); if (d<best){best=d; bu=gLeafU;} d = sdRing(r, ang, p.y, n0-8.0, 4.79992, 1.00+tiltBias, 0.48, wide*0.65, thick, 0.13); if (d<best){best=d; bu=gLeafU;} gLeafU = bu; float bud = length(p - vec3(0.0, 0.22, 0.0)) - 0.12; if (bud < best){ best = bud; gLeafU = 1.2; } return best; } // jelly-bean sedum: a low dome of plump beads float sdBeads(float r, float ang, float py, float N, float off, float R, float lift, float tilt, float br){ float sec = 2.0*PI/N; float a = mod(ang + off, sec) - 0.5*sec; vec3 q = vec3(r*cos(a) - R, py - lift, r*sin(a)); q.xy *= rot(-tilt); // bead points outward-up float d = length(q*vec3(0.62, 1.0, 1.0)) - br; gLeafU = clamp(0.5 + 0.34*q.x/br, 0.0, 1.0); // ~1 only at the bead's outer tip return d; } float sdCluster(vec3 p, vec3 h){ float best = 1e5; float bu = 0.0; float d; float r = length(p.xz); float ang = atan(p.z, p.x); d = sdBeads(r, ang, p.y, 10.0, 0.0, 0.22, 0.05, 0.50, 0.075); if (d<best){best=d; bu=gLeafU;} d = sdBeads(r, ang, p.y, 6.0, 0.35, 0.13, 0.14, 0.95, 0.070); if (d<best){best=d; bu=gLeafU;} d = sdBeads(r, ang, p.y, 3.0, 1.10, 0.05, 0.22, 1.35, 0.065); if (d<best){best=d; bu=gLeafU;} gLeafU = bu; return best; } float ground(vec2 xz){ return -0.02 + 0.04*noise2(xz*1.7); } // centre + size of the rosette in cell id (cheap: no ground lookup) vec3 plantCenter(vec2 id, out float s){ vec2 h2 = hash22(id); s = mix(0.78, 1.0, fract(h2.x*5.17 + h2.y*3.91)); vec2 c = (id + 0.5)*CELL + (h2 - 0.5)*0.16; return vec3(c.x, -0.04, c.y); } vec3 gTmp; float plantDist(vec3 p, vec2 id){ float s; vec3 cen = plantCenter(id, s); cen.y += ground(cen.xz); vec3 h = vec3(hash22(id + 7.7), hash21(id + 3.3)); if (abs(id.x) + abs(id.y) < 0.5){ h.x = 0.1; h.y = 0.15; s = 1.0; } // hero plant vec3 q = p - cen; q.xz *= rot(h.z*6.2831); gTmp = h; return sdRosette(q / s, h) * s; } float map(vec3 p){ float dg = p.y - ground(p.xz); gMat = 0.0; vec2 id = floor(p.xz / CELL); // --- neighbouring cells: bounding spheres, and the nearest one gets a full evaluation --- vec2 f = p.xz/CELL - id - 0.5; vec2 sg = step(0.0, f)*2.0 - 1.0; float s1, s2, s3; vec3 lift = vec3(0.0, 0.3, 0.0); float br = clamp(0.6 + 0.3*leafCurl + 0.1*leafWidth, 1.02, 1.18); // bounding radius follows the leaf controls float b1 = length(p - plantCenter(id + vec2(sg.x, 0.0), s1) - lift) - br*s1; float b2 = length(p - plantCenter(id + vec2(0.0, sg.y), s2) - lift) - br*s2; float b3 = length(p - plantCenter(id + sg, s3) - lift) - br*s3; vec2 idN = id + vec2(sg.x, 0.0); float bO = min(b2, b3); if (b2 < b1 && b2 < b3){ idN = id + vec2(0.0, sg.y); bO = min(b1, b3); } if (b3 < b1 && b3 < b2){ idN = id + sg; bO = min(b1, b2); } // --- rosettes: this cell and the nearest neighbour --- float dr = plantDist(p, id); float lu0 = gLeafU; vec3 h = gTmp; float drN = plantDist(p, idN); if (drN < dr){ dr = drN; lu0 = gLeafU; h = gTmp; } gLeafU = lu0; float d = dg; if (dr < d){ d = dr; gMat = (gLeafU > 1.1) ? 2.0 : 1.0; gCell = h; } // --- sedum clusters on the cell corners (about 70% of corners) --- vec2 cid = floor(p.xz / CELL + 0.5); vec2 hc2 = hash22(cid + 51.3); float lu = gLeafU; if (hc2.x > 0.3){ vec2 cc = cid*CELL + (hc2 - 0.5)*0.2; float sc = mix(0.75, 1.1, hc2.y); vec3 qc = p - vec3(cc.x, ground(cc) - 0.05, cc.y); qc.xz *= rot(hc2.x*9.0); float dc = sdCluster(qc / sc, hc2.xyy) * sc; if (dc < d){ d = dc; gMat = 3.0; gCell = vec3(hc2, hash21(cid)); lu = gLeafU; } } gLeafU = lu; gReal = d; float b = bO; // other corners' clusters are at least (CELL - dist to nearest corner) away in xz vec2 g = p.xz/CELL - cid; vec2 sgc = step(0.0, g)*2.0 - 1.0; float bc = min(length(g - vec2(sgc.x, 0.0)), length(g - vec2(0.0, sgc.y)))*CELL - 0.5; bc = max(bc, p.y - 0.45); b = min(b, bc); return min(d, max(b, 0.005)); } vec3 calcNormal(vec3 p){ vec2 e = vec2(0.0012, -0.0012); return normalize(e.xyy*map(p + e.xyy) + e.yyx*map(p + e.yyx) + e.yxy*map(p + e.yxy) + e.xxx*map(p + e.xxx)); } float softShadow(vec3 ro, vec3 rd){ float res = 1.0; float t = 0.03; for (int i = 0; i < 16; i++){ float d = map(ro + rd*t); res = min(res, 5.0*gReal/t); t += clamp(d, 0.03 + 0.08*t, 0.4); // step floor grows with distance so the ray escapes the thicket if (res < 0.02 || t > 5.0) break; } return clamp(res, 0.0, 1.0); } float calcAO(vec3 p, vec3 n){ float occ = 0.0, sca = 1.0; for (int i = 0; i < 3; i++){ float h = 0.04 + 0.14*float(i); map(p + n*h); occ += (h - gReal)*sca; sca *= 0.75; } return clamp(1.0 - 2.2*occ, 0.0, 1.0); } void main(){ vec2 uv = (gl_FragCoord.xy - 0.5*RENDERSIZE.xy) / RENDERSIZE.y; float ph = fract(TIME*orbitSpeed/PERIOD); float th = 2.0*PI*ph; // camera: slow orbit around the hero plant in cell (0,0), low and close float hs; vec3 target = plantCenter(vec2(0.0), hs); target.y = camAim; vec3 ro = target + vec3(camDist*cos(th), camHeight, camDist*sin(th)); vec3 fw = normalize(target - ro); vec3 rt = normalize(cross(fw, vec3(0.0, 1.0, 0.0))); vec3 up = cross(rt, fw); vec3 rd = normalize(uv.x*rt + uv.y*up + lens*fw); // march float t = 0.0; float hit = -1.0; float dl = 1.0; for (int i = 0; i < 64; i++){ dl = map(ro + rd*t); if (dl < 0.0008*t){ hit = t; break; } t += dl*0.9; if (t > 24.0) break; } if (hit < 0.0 && t < 24.0 && dl < 0.08) hit = t; // ran out of steps while grazing a surface float sunAz = sunPos.x*2.0*PI; vec3 sunDir = normalize(vec3(0.806*cos(sunAz), mix(0.1, 1.2, sunPos.y), 0.806*sin(sunAz))); vec3 skyCol = vec3(0.55, 0.68, 0.85); vec3 col = mix(skyCol, vec3(0.95, 0.80, 0.62), clamp(1.0 - 3.0*rd.y, 0.0, 1.0)); if (hit > 0.0){ vec3 p = ro + rd*hit; map(p); // refresh material globals at the hit float mat = gMat; float lu = gLeafU; vec3 cell = gCell; vec3 n = calcNormal(p); if (mat > 0.5 && mat < 1.5){ float st = noise2(vec2(p.y*18.0 + cell.z*40.0, atan(p.z - 0.5, p.x + 0.3)*14.0)) - 0.5; n = normalize(n + 0.10*st*vec3(noise2(p.xz*30.0) - 0.5, 0.0, noise2(p.zx*30.0) - 0.5)); } float dew = 0.0; if (mat > 0.5 && mat < 1.5){ vec3 dc = floor(p*24.0); vec3 hd = vec3(hash22(dc.xy + dc.z*3.7), hash21(dc.xz + dc.y*1.3)); if (hd.z > 1.0 - 0.12*dewAmount){ vec3 cpt = (dc + 0.3 + 0.4*hd)/24.0; float rr = 0.009 + 0.010*hd.x; vec3 dv = p - cpt; dv -= n*dot(dv, n); // project onto the leaf: a hemisphere sitting on the surface float q = length(dv)/rr; if (q < 1.0){ float hg = sqrt(1.0 - q*q); dew = 0.4 + 0.6*hg; n = normalize(n*hg + dv/rr); } } } vec3 base; if (mat < 0.5){ // gravel: jittered pebbles of varied size over dark soil float g1 = noise2(p.xz*3.0); vec2 gp = p.xz*13.0; vec2 gi = floor(gp), gf = fract(gp); float pd = 1.0; float pid = 0.0; float psz = 0.1; for (int j = 0; j < 4; j++){ vec2 o = vec2(float(j - 2*(j/2)), float(j/2)) - step(0.5, gf); // the 2x2 nearest cells vec2 cc = gi + o; vec2 hh = hash22(cc); vec2 r = o + 0.5 + 0.9*(hh - 0.5) - gf; float dd = dot(r, r); if (dd < pd && hh.y > 0.42){ pd = dd; pid = hash21(cc + 9.1); psz = 0.04 + 0.16*hh.x*hh.x; } } float pebble = smoothstep(psz, psz*0.45, pd) * 0.6; vec3 soil = mix(vec3(0.21, 0.16, 0.12), vec3(0.34, 0.27, 0.20), g1) * (0.8 + 0.4*noise2(p.xz*40.0)); vec3 stone = mix(vec3(0.30, 0.25, 0.21), vec3(0.46, 0.40, 0.34), pid); stone = mix(stone, vec3(0.60, 0.48, 0.42), smoothstep(0.7, 0.9, fract(pid*7.0))*0.6); base = mix(soil, stone, pebble); } else if (mat < 1.5){ // discrete cultivar palettes: no three-way hue mixing float k = floor(fract(cell.y*7.31)*3.0); vec3 body = k < 0.5 ? vec3(0.24, 0.54, 0.47) : (k < 1.5 ? vec3(0.44, 0.62, 0.15) : vec3(0.56, 0.36, 0.58)); vec3 tip = k < 0.5 ? blushColor.rgb : (k < 1.5 ? vec3(0.80, 0.30, 0.10) : vec3(0.75, 0.25, 0.45)); base = mix(body, tip, clamp(blush * smoothstep(0.5, 1.0, lu) * (0.45 + 0.55*cell.x), 0.0, 1.0)); base = mix(base, body*vec3(0.8, 0.95, 0.75), smoothstep(0.3, 0.0, lu)); // darker toward the base } else if (mat < 2.5){ base = vec3(0.58, 0.70, 0.40); } else { // jelly beans: lime with sun-blushed red tips vec3 body = mix(vec3(0.45, 0.68, 0.20), vec3(0.62, 0.72, 0.28), cell.z); base = mix(body, vec3(0.85, 0.25, 0.10), smoothstep(0.78, 1.0, lu) * (0.5 + 0.5*cell.y)); } float ndl = clamp(dot(n, sunDir), 0.0, 1.0); float sh = softShadow(p + n*0.012, sunDir); float ao = calcAO(p, n); float ndv = clamp(dot(n, -rd), 0.0, 1.0); float fres = pow(1.0 - ndv, 1.5); vec3 sun = vec3(1.0, 0.86, 0.66) * 1.32; vec3 sky = vec3(0.45, 0.60, 0.85) * (0.55 + 0.45*n.y) * 0.62; vec3 bounce = vec3(0.40, 0.28, 0.18) * clamp(-n.y, 0.0, 1.0) * 0.6; col = base * (sun*ndl*sh + sky*ao + bounce*ao); if (mat > 0.5){ vec3 hv = normalize(sunDir - rd); float spec = pow(clamp(dot(n, hv), 0.0, 1.0), 48.0) * sh; col += vec3(1.0, 0.9, 0.8) * spec * 0.45; if (dew > 0.0){ float dsp = pow(clamp(dot(n, hv), 0.0, 1.0), 120.0); float dnv = clamp(dot(n, -rd), 0.0, 1.0); col = col*(0.55 + 0.35*dnv) + vec3(1.0, 0.97, 0.9)*dsp*3.0 + skyCol*0.5*pow(1.0 - dnv, 3.0); } col += skyCol * fres * 0.12 * ao; // waxy bloom sheen col += base * vec3(1.2, 0.8, 0.6) * 0.35 * clamp(dot(-n, sunDir), 0.0, 1.0) * sh * ao; // back-lit translucency } col = mix(col, vec3(0.82, 0.72, 0.62), 1.0 - exp(-0.016*haze*hit)); } col *= 1.0 - 0.35*dot(uv, uv); // soft vignette col = mix(col, col*col*(3.0 - 2.0*col), 0.25); // gentle contrast col = 1.0 - exp(-col*1.15); col = mix(vec3(dot(col, vec3(0.299,0.587,0.114))), col, 1.18); // a little saturation col = pow(col, vec3(0.4545)); gl_FragColor = vec4(col, 1.0); }
forking #xss76xwa — lineage will be recorded.
renders in YOUR browser. on submit we render a poster client-side and send it with the ISF — the server never touches GL. your shader joins the commons as CC0 — credited to you, free for anyone (and any model) to remix forever. that's the whole point. it's permanent: once it's public, CC0 has no take-backs. by submitting you attest you have the right to release it as CC0.