{
  "license": "CC0-1.0",
  "shader": {
    "id": "8rmrb9yd",
    "title": "The Tidal Cathedral II",
    "license": "CC0-1.0",
    "provenance_type": "witnessed_generation",
    "made_with": "OpenAI Codex; human art direction; glslop.mjs and the glslop",
    "status": "active",
    "compile_ok": true,
    "render_hash": "1204000895efbfff",
    "tags": [
      "landscape",
      "raymarching",
      "3d",
      "mountains",
      "water",
      "nature",
      "cinematic",
      "multipass"
    ],
    "parents": [
      "nttv4izm"
    ],
    "author": "zazi5inxhmgi",
    "author_name": "ufffd",
    "author_url": "/u/ufffd",
    "created_at": "2026-09-05T01:25:25.114Z",
    "poster_url": "/posters/8rmrb9yd.avif",
    "stats": {
      "slops": 0,
      "views": 105,
      "forks": 0
    },
    "self": "/api/v1/shaders/8rmrb9yd",
    "isf": "/*{\n  \"DESCRIPTION\": \"THE TIDAL CATHEDRAL II — the original procedural fjord refined with human feedback: stable shoreline tracing, weathered mineral surfaces, smaller conifers and richer alpine detail. Procedural geometry, lighting and internal caches; no imported textures. A second study of the original composition.\",\n  \"CREDIT\": \"OpenAI / Codex\",\n  \"ISFVSN\": \"2\",\n  \"CATEGORIES\": [\"Landscape\", \"Raymarching\"],\n  \"PASSES\": [\n    { \"TARGET\": \"geologyNoise\", \"PERSISTENT\": true },\n    { \"TARGET\": \"forestData\", \"PERSISTENT\": true, \"FLOAT\": true },\n    {}\n  ],\n  \"INPUTS\": [\n    { \"NAME\": \"cameraDrift\", \"LABEL\": \"Camera drift\", \"TYPE\": \"float\", \"DEFAULT\": 0.65, \"MIN\": 0.0, \"MAX\": 1.0 },\n    { \"NAME\": \"sunHeight\", \"LABEL\": \"Sun elevation\", \"TYPE\": \"float\", \"DEFAULT\": 0.27, \"MIN\": 0.12, \"MAX\": 0.85 },\n    { \"NAME\": \"atmosphere\", \"LABEL\": \"Valley mist\", \"TYPE\": \"float\", \"DEFAULT\": 0.5, \"MIN\": 0.0, \"MAX\": 1.5 },\n    { \"NAME\": \"waterMotion\", \"LABEL\": \"Water motion\", \"TYPE\": \"float\", \"DEFAULT\": 0.6, \"MIN\": 0.0, \"MAX\": 1.5 },\n    { \"NAME\": \"exposure\", \"LABEL\": \"Exposure\", \"TYPE\": \"float\", \"DEFAULT\": 0.95, \"MIN\": 0.6, \"MAX\": 1.6 },\n    { \"NAME\": \"showBoat\", \"LABEL\": \"Sailboat\", \"TYPE\": \"bool\", \"DEFAULT\": true }\n  ]\n}*/\n\n#define FAR 290.0\nconst mat2 TURN = mat2(0.80, -0.60, 0.60, 0.80);\nconst float FOREST_SPACING = 1.45;\n\nfloat hash12(vec2 p) {\n    vec3 q = fract(vec3(p.xyx) * 0.1031);\n    q += dot(q, q.yzx + 33.33);\n    return fract((q.x + q.y) * q.z);\n}\n\nfloat hash13(vec3 p) {\n    p = fract(p * 0.1031);\n    p += dot(p, p.zyx + 31.32);\n    return fract((p.x + p.y) * p.z);\n}\n\nfloat noise2(vec2 p) {\n    vec2 i = floor(p), f = fract(p);\n    f = f * f * (3.0 - 2.0 * f);\n    vec2 at = mod(i + 32.0, 64.0) + 0.5;\n    float a = IMG_PIXEL(geologyNoise, at).g;\n    float b = IMG_PIXEL(geologyNoise, at + vec2(1,0)).g;\n    float c = IMG_PIXEL(geologyNoise, at + vec2(0,1)).g;\n    float d = IMG_PIXEL(geologyNoise, at + vec2(1,1)).g;\n    return mix(mix(a,b,f.x),mix(c,d,f.x),f.y);\n}\n\nfloat noise3(vec3 p) {\n    vec3 i = floor(p), f = fract(p);\n    f = f * f * (3.0 - 2.0 * f);\n    i = mod(i, 16.0);\n    vec2 tile0 = vec2(mod(i.z, 4.0), floor(i.z / 4.0));\n    vec2 at = tile0 * 18.0 + i.xy + 1.5;\n    vec2 a = IMG_PIXEL(geologyNoise, at).rb;\n    vec2 b = IMG_PIXEL(geologyNoise, at + vec2(1,0)).rb;\n    vec2 c = IMG_PIXEL(geologyNoise, at + vec2(0,1)).rb;\n    vec2 d = IMG_PIXEL(geologyNoise, at + vec2(1,1)).rb;\n    vec2 pair = mix(mix(a,b,f.x),mix(c,d,f.x),f.y);\n    return mix(pair.x, pair.y, f.z);\n}\n\n// Value and analytic gradient from the same lattice. The derivatives remain\n// smooth without six additional noise samples or a screen-space approximation.\nvec4 noiseGradient(vec3 p) {\n    vec3 i=mod(floor(p),16.0), f=fract(p);\n    vec3 df=6.0*f*(1.0-f);\n    f=f*f*(3.0-2.0*f);\n    vec2 at=vec2(mod(i.z,4.0),floor(i.z/4.0))*18.0+i.xy+1.5;\n    vec2 a=IMG_PIXEL(geologyNoise,at).rb;\n    vec2 b=IMG_PIXEL(geologyNoise,at+vec2(1,0)).rb;\n    vec2 c=IMG_PIXEL(geologyNoise,at+vec2(0,1)).rb;\n    vec2 d=IMG_PIXEL(geologyNoise,at+vec2(1,1)).rb;\n    vec2 layers=mix(mix(a,b,f.x),mix(c,d,f.x),f.y);\n    vec2 dx=mix(b-a,d-c,f.y)*df.x;\n    vec2 dy=mix(c-a,d-b,f.x)*df.y;\n    return vec4(mix(layers.x,layers.y,f.z),mix(dx.x,dx.y,f.z),mix(dy.x,dy.y,f.z),(layers.y-layers.x)*df.z);\n}\n\nfloat fracturedNoise(vec3 p) {\n    vec3 i=mod(floor(p),16.0),f=fract(p);\n    vec2 at=vec2(mod(i.z,4.0),floor(i.z/4.0))*18.0+i.xy+1.5;\n    vec2 a=IMG_PIXEL(geologyNoise,at).rb;\n    vec2 b=IMG_PIXEL(geologyNoise,at+vec2(1,0)).rb;\n    vec2 c=IMG_PIXEL(geologyNoise,at+vec2(0,1)).rb;\n    vec2 d=IMG_PIXEL(geologyNoise,at+vec2(1,1)).rb;\n    vec2 pair=mix(mix(a,b,f.x),mix(c,d,f.x),f.y);\n    return mix(pair.x,pair.y,f.z);\n}\n\n// Three-dimensional joint planes and their gradient. The two closest stone\n// cells define a fissure; unlike painted contour lines it changes illumination.\nvec4 stoneJoints(vec3 p) {\n    vec3 cell=floor(p), f=fract(p);\n    float d1=100.0,d2=100.0;\n    vec3 v1=vec3(0),v2=vec3(0);\n    for(int i=0;i<27;i++) {\n        float fi=float(i);\n        vec3 o=vec3(mod(fi,3.0),mod(floor(fi/3.0),3.0),floor(fi/9.0))-1.0;\n        vec3 c=cell+o;\n        vec3 point=0.18+0.64*vec3(hash13(c),hash13(c+19.7),hash13(c+41.3));\n        vec3 v=o+point-f;\n        float d=dot(v,v);\n        if(d<d1){d2=d1;v2=v1;d1=d;v1=v;}else if(d<d2){d2=d;v2=v;}\n    }\n    float a=sqrt(max(d1,0.00001)), b=sqrt(max(d2,0.00001));\n    return vec4(b-a,v1/a-v2/b);\n}\n\nvec4 rockRelief(vec3 p,float footprint) {\n    vec3 gradient=vec3(0.0);\n    float height=0.0,frequency=1.8,amplitude=0.24;\n    for(int i=0;i<4;i++) {\n        float filtering=1.0-smoothstep(0.35,1.15,footprint*frequency);\n        vec4 v=noiseGradient(p*frequency+vec3(9.7,2.3,5.1)*float(i));\n        float softness=sqrt((v.x-0.5)*(v.x-0.5)+0.0025);\n        float signum=(v.x-0.5)/softness;\n        height+=(0.5-softness)*amplitude*filtering;\n        gradient-=v.yzw*signum*frequency*amplitude*filtering;\n        frequency*=2.85;amplitude*=0.43;\n    }\n    return vec4(height,gradient);\n}\n\nfloat fbm2(vec2 p) {\n    float v = 0.0, a = 0.5;\n    for (int i = 0; i < 5; i++) {\n        v += a * noise2(p);\n        p = TURN * p * 2.07 + vec2(17.4, 9.2);\n        a *= 0.48;\n    }\n    return v;\n}\n\nfloat fbm3(vec3 p) {\n    float v = 0.0, a = 0.5;\n    for (int i = 0; i < 4; i++) {\n        v += a * noise3(p);\n        p = p.yzx * 2.03 + vec3(11.7, 4.3, 8.1);\n        a *= 0.48;\n    }\n    return v;\n}\n\nfloat ridge(vec2 p) {\n    float v = 0.0, a = 0.55, weight = 1.0;\n    for (int i = 0; i < 4; i++) {\n        float r = 1.0 - abs(2.0 * noise2(p) - 1.0);\n        r *= r;\n        v += a * r * weight;\n        weight = clamp(r * 1.8, 0.15, 1.0);\n        p = TURN * p * 2.11 + 3.7;\n        a *= 0.49;\n    }\n    return v;\n}\n\nfloat riverPath(float z) {\n    return 4.0 + 5.0 * sin(z * 0.045) - 12.0 * smoothstep(42.0, 80.0, z);\n}\n\nfloat terrain(vec2 p) {\n    float channel = riverPath(p.y);\n    float bank = smoothstep(9.0, 25.0, abs(p.x - channel));\n    float back = smoothstep(42.0, 80.0, p.y);\n    float r = ridge(p * 0.035 + vec2(2.8, 7.3));\n    float h = -2.7 + bank * (3.8 + 12.0 * fbm2(p * 0.085));\n    float peaks = max(0.0, 1.0 - length((p - vec2(-39.0, 105.0)) * vec2(0.020, 0.019))) * 48.0;\n    peaks = max(peaks, max(0.0, 1.0 - length((p - vec2(43.0, 123.0)) * vec2(0.021, 0.016))) * 57.0);\n    peaks = max(peaks, max(0.0, 1.0 - length((p - vec2(-5.0, 178.0)) * vec2(0.014, 0.015))) * 72.0);\n    peaks = max(peaks, max(0.0, 1.0 - length((p - vec2(-89.0, 152.0)) * vec2(0.020, 0.016))) * 61.0);\n    h += back * (4.0 + peaks * (0.72 + 0.48 * r) + 5.0 * r);\n    h *= 1.0 - 0.55 * exp(-abs(p.x - channel) * 0.3) * back;\n    vec2 nearRock = (p - vec2(-11.0, -13.0)) / vec2(9.5, 11.0);\n    h += 7.5 * exp(-dot(nearRock, nearRock) * 1.5);\n    h += 0.12 * noise2(p * 2.8);\n    return h;\n}\n\n// Elliptical stone vault, leaned and twisted once in world space.\n// The enclosing box avoids evaluating erosion noise outside the arch's region.\nfloat arch(vec3 p) {\n    vec3 q = p - vec3(-7.0, 2.0, 27.0);\n    vec3 b = abs(q) - vec3(21.0, 24.0, 9.0);\n    float bound = length(max(b, 0.0));\n    if (bound > 2.0) return bound + 0.2;\n    q.x += q.y * 0.15 + sin(q.y * 0.24) * 0.65;\n    q.z += q.x * 0.15;\n    q.y += sin(q.x * 0.19) * 1.1;\n    float thick = 2.15 + 1.1 * (1.0 - smoothstep(-12.0, 0.0, q.x)) + 0.65 * noise3(p * 0.17);\n    float d = length(vec2(length(q.xy * vec2(1.0, 0.76)) - 12.0, q.z * 1.3)) - thick;\n    d += (noise3(p * 0.46) - 0.5) * 1.65;\n    if(d>0.5) return (d-0.195)*0.32;\n    vec3 chips=vec3(p.x+0.31*p.z,p.y+0.13*p.x,p.z-0.27*p.y);\n    d += (fracturedNoise(chips * 2.0) - 0.5) * 0.32;\n    d += (fracturedNoise(chips * 5.9+7.1) - 0.5) * 0.07;\n    return d * 0.32;\n}\n\nvec3 boatSpace(vec3 p) {\n    p-=vec3(7.0+0.14*sin(TIME*0.10),0.07+0.009*sin(TIME*1.25),7.0);\n    p.xz=mat2(0.94,-0.342,0.342,0.94)*p.xz;\n    p.xy=mat2(cos(0.025*sin(TIME*1.1)),-sin(0.025*sin(TIME*1.1)),sin(0.025*sin(TIME*1.1)),cos(0.025*sin(TIME*1.1)))*p.xy;\n    return p;\n}\n\nfloat boat(vec3 p) {\n    float bound=length(p-vec3(7.0+0.14*sin(TIME*0.10),0.57+0.009*sin(TIME*1.25),7.0))-1.16;\n    if(bound>0.4) return bound;\n    vec3 q=boatSpace(p);\n    float hull=max((length(q/vec3(0.49,0.11,0.145))-1.0)*0.1,q.y-0.035);\n    vec3 pole=q-vec3(-0.08,0.57,0.0);\n    float mast=max(length(pole.xz)-0.008,abs(pole.y)-0.57);\n    vec2 sail=q.xy-vec2(-0.065,0.18);\n    float mainSail=max(max(-sail.x,-sail.y),dot(sail,vec2(0.882,0.471))-0.405);\n    mainSail=max(mainSail,abs(q.z-0.017-0.035*sin(max(sail.y,0.0)*3.4))-0.009);\n    vec2 jib=vec2(-q.x-0.10,q.y-0.15);\n    float frontSail=max(max(-jib.x,-jib.y),dot(jib,vec2(0.922,0.388))-0.32);\n    frontSail=max(frontSail,abs(q.z+0.015)-0.006);\n    return min(min(hull,mast),min(mainSail,frontSail))*0.65;\n}\n\nvec2 scene(vec3 p) {\n    float ground = terrain(p.xz);\n    float land = (p.y - ground) * 0.23;\n    float vault = arch(p);\n    vec2 res = land < vault ? vec2(land, 1.0) : vec2(vault, 2.0);\n    if(showBoat) { float vessel=boat(p);if(vessel<res.x)res=vec2(vessel,4.0); }\n    if (p.y - ground < 3.0 && p.y < 18.0 && p.z > -8.0 && p.z < 106.0) {\n        vec2 cell = floor(p.xz / FOREST_SPACING);\n        vec2 lookup = cell + vec2(64.0,16.0);\n        vec4 data = IMG_PIXEL(forestData, lookup + 0.5);\n        float seed = data.b;\n        vec2 center = (cell + 0.5) * FOREST_SPACING;\n        center += vec2(seed - 0.5, hash12(cell + 8.1) - 0.5) * 0.72;\n        float ht = data.g;\n        if (ht > 0.1 && lookup.x >= 0.0 && lookup.x < 128.0 && lookup.y >= 0.0 && lookup.y < 96.0) {\n            vec3 q = p - vec3(center.x, data.r - 0.05, center.y);\n            q.x += 0.024 * sin(q.y * 3.0 + seed * 8.0);\n            float crown = 0.28 * ht * max(1.0 - q.y / ht, 0.0);\n            crown *= 0.80 + 0.16 * sin(q.y * 27.0 + seed * 11.0) + 0.18 * noise3(q * 21.0);\n            float foliage = max(length(q.xz) - crown, max(0.13*ht-q.y, q.y - ht));\n            float trunk = max(length(q.xz)-0.022*ht,max(-q.y,q.y-ht*0.82));\n            float tree = min(foliage,trunk) * 0.48;\n            if (tree < res.x) res = vec2(tree, 3.0);\n        }\n    }\n    return res;\n}\n\nvec2 trace(vec3 ro, vec3 rd, float limit, bool reflected) {\n    float t = 0.06;\n    float mat = -1.0;\n    for (int i = 0; i < 768; i++) {\n        vec2 h = scene(ro + rd * t);\n        float eps = max(0.004, t * (reflected ? 0.0014 : 0.00055));\n        if (h.x < eps) { mat = h.y; break; }\n        t += max(h.x * 0.91, 0.008);\n        if (t > limit || (reflected && i > 383)) break;\n    }\n    return vec2(min(t, limit), mat);\n}\n\nvec3 normalAt(vec3 p, float t) {\n    float e = max(0.008, t * 0.45 / max(RENDERSIZE.y,360.0));\n    vec3 n = vec3(0.0);\n    for (int i = 0; i < 4; i++) {\n        vec3 k = i == 0 ? vec3(1,-1,-1) : (i == 1 ? vec3(-1,-1,1) : (i == 2 ? vec3(-1,1,-1) : vec3(1,1,1)));\n        n += k * scene(p + k * e).x;\n    }\n    return normalize(n);\n}\n\nvec3 sunDirection() { return normalize(vec3(-0.65, sunHeight, -0.50)); }\n\nfloat shadowAt(vec3 p, vec3 lightDir) {\n    float sh = 1.0, t = 0.13;\n    for (int i = 0; i < 36; i++) {\n        float h = scene(p + lightDir * t).x;\n        sh = min(sh, 12.0 * max(h, 0.0) / t);\n        t += clamp(h, 0.12, 4.0);\n        if (sh < 0.015 || t > 65.0) break;\n    }\n    return clamp(sh, 0.0, 1.0);\n}\n\nvec3 sky(vec3 rd, bool clouds) {\n    vec3 sd = sunDirection();\n    float y = max(rd.y, 0.0);\n    float sun = max(dot(rd, sd), 0.0);\n    vec3 horizon = mix(vec3(0.48,0.60,0.67), vec3(0.82,0.56,0.34), smoothstep(0.15,-0.8,rd.x) * 0.8);\n    vec3 col = mix(horizon, vec3(0.018, 0.085, 0.19), pow(y, 0.42));\n    col += vec3(1.0, 0.49, 0.19) * pow(sun, 8.0) * 0.32;\n    col += vec3(1.0, 0.65, 0.28) * pow(sun, 70.0) * 0.4;\n    col += vec3(9.0, 6.4, 3.6) * smoothstep(0.9995, 0.99985, sun);\n    if (clouds && rd.y > 0.015) {\n        float trans = 1.0;\n        vec3 cloudCol = vec3(0.0);\n        float start = 70.0 / rd.y;\n        float stride = 22.0 / (rd.y * 9.0);\n        for (int i = 0; i < 9; i++) {\n            vec3 p = rd * (start + (float(i) + 0.5) * stride);\n            p.x += TIME * 0.7;\n            vec3 cp = p * vec3(0.010, 0.035, 0.010);\n            float shape = fbm3(cp * 1.5 + vec3(1.1, 0, 3.0));\n            float edge = smoothstep(70.0, 76.0, p.y) * (1.0 - smoothstep(84.0, 92.0, p.y));\n            float den = smoothstep(0.46, 0.66, shape) * edge;\n            float a = (1.0 - exp(-den * stride * 0.16)) * smoothstep(0.03, 0.19, rd.y);\n            float rim = pow(sun, 12.0);\n            vec3 lighting = mix(vec3(0.25, 0.33, 0.42), vec3(0.95, 0.87, 0.76), clamp(0.45 + (0.59 - shape) * 3.0 + rim * 0.5, 0.0, 1.0));\n            cloudCol += trans * a * lighting;\n            trans *= 1.0 - a;\n        }\n        col = col * trans + cloudCol;\n        // High, stretched cirrus adds a second scale to the sky.\n        vec2 cp = rd.xz / max(rd.y, 0.05) * vec2(0.9, 4.5) + vec2(TIME * 0.006, 0);\n        float veil = smoothstep(0.55, 0.76, fbm2(cp));\n        col = mix(col, vec3(0.91, 0.9, 0.85), veil * 0.22);\n    }\n    return col;\n}\n\nvec3 haze(vec3 col, vec3 ro, vec3 rd, float t) {\n    float dy = rd.y * 0.10;\n    float integral = abs(dy) < 0.0001 ? t : (1.0 - exp(-dy * t)) / dy;\n    float mist = integral * exp(-max(ro.y, 0.0) * 0.10);\n    float optical = t * 0.0014 + atmosphere * 0.006 * max(mist, 0.0);\n    float lit = pow(max(dot(rd, sunDirection()), 0.0), 6.0);\n    vec3 air = mix(vec3(0.32, 0.50, 0.59), vec3(1.1, 0.82, 0.50), lit * 0.72);\n    return mix(col, air, 1.0 - exp(-optical));\n}\n\nvec3 shadeRock(vec3 p, vec3 rd, float t, float material, bool reflected) {\n    vec3 geometricNormal = normalAt(p, t);\n    vec3 n=geometricNormal;\n    vec3 sd = sunDirection();\n    if(material>3.5) {\n        vec3 q=boatSpace(p);\n        vec3 paint=q.y<0.045?vec3(0.035,0.075,0.105):vec3(0.72,0.60,0.40);\n        if(abs(q.x+0.08)<0.015)paint=vec3(0.17,0.115,0.065);\n        float seams=1.0-0.10*(1.0-smoothstep(0.015,0.05,abs(sin(q.y*24.0+q.x*4.0))));\n        return paint*seams*(vec3(0.44,0.49,0.53)+vec3(1.5,1.2,0.80)*abs(dot(n,sd)));\n    }\n    if (material > 2.5) {\n        float sh = shadowAt(p + n * 0.12, sd);\n        float needle = noise3(p * 18.0);\n        vec3 green = mix(vec3(0.018, 0.038, 0.021), vec3(0.080, 0.115, 0.041), needle);\n        vec3 light = vec3(0.32, 0.47, 0.58) + vec3(1.5, 1.05, 0.54) * max(dot(n, sd), 0.0) * sh;\n        light += vec3(0.28, 0.36, 0.12) * pow(max(dot(rd, sd), 0.0), 3.0);\n        return green * light;\n    }\n    float footprint=t/(RENDERSIZE.y*1.08);\n    vec4 relief=rockRelief(p,footprint*(reflected?1.8:1.0));\n    vec3 jointScale=vec3(0.82,1.75,0.82);\n    float jointWidth=max(0.014,footprint*0.25);\n    float broken=smoothstep(0.42,0.67,noise3(p*0.55+21.0));\n    vec4 joints=vec4(10.0,0.0,0.0,0.0);\n    if(broken>0.01 && footprint<0.24)joints=stoneJoints(p*jointScale+vec3(0.4,1.8,2.3));\n    float crevice=exp(-joints.x/jointWidth)*broken*(1.0-smoothstep(0.09,0.24,footprint));\n    vec3 fractureGradient=0.012/jointWidth*crevice*joints.yzw*jointScale;\n    float bedCoordinate=dot(p,vec3(0.27,2.25,0.13));\n    float bedding=sin(bedCoordinate*6.2831+noise3(p*0.28)*2.0);\n    float bedFilter=1.0-smoothstep(0.05,0.22,footprint);\n    vec3 bedGradient=vec3(0.27,2.25,0.13)*0.035*cos(bedCoordinate*6.2831)*bedFilter;\n    vec3 gradient=relief.yzw+fractureGradient+bedGradient;\n    gradient-=geometricNormal*dot(gradient,geometricNormal);\n    float grain=fbm3(p*vec3(1.7,0.22,1.7));\n    float smallGrain=noise3(p*8.5);\n    float moss=smoothstep(0.38,0.78,geometricNormal.y+(grain-0.45)*0.55);\n    moss*=1.0-smoothstep(17.0,26.0,p.y);\n    n=normalize(n-gradient*mix(1.15,0.40,moss));\n    float iron=smoothstep(0.53,0.75,fbm3(p*0.30+11.0));\n    float mineral=clamp((grain-0.43)*2.7+0.46,0.0,1.0);\n    vec3 stone=mix(vec3(0.13,0.135,0.13),vec3(0.43,0.39,0.31),mineral);\n    stone=mix(stone,vec3(0.31,0.19,0.10),iron*0.55);\n    stone*=0.9+0.18*smallGrain+0.025*bedding;\n    stone*=1.0-0.19*crevice*(1.0-moss);\n    vec3 vegetation=mix(vec3(0.026,0.058,0.012),vec3(0.17,0.225,0.040),grain);\n    vegetation*=0.78+0.42*smallGrain;\n    stone=mix(stone,vegetation,moss);\n    float snowLine = p.y - 25.0 + 5.0 * fbm2(p.xz * 0.16);\n    float snow = smoothstep(0.0, 4.0, snowLine) * smoothstep(0.35, 0.82, geometricNormal.y + (grain - 0.5) * 0.35);\n    if (material > 1.5) snow = 0.0;\n    stone = mix(stone, vec3(0.62, 0.72, 0.79)*(0.92+0.08*smallGrain), snow);\n    n=normalize(mix(n,geometricNormal,snow*0.70));\n    float wet = 1.0 - smoothstep(0.15, 1.7, p.y);\n    stone *= 1.0 - wet * 0.5;\n    float ao = 1.0;\n    if (!reflected) {\n        float occ = 0.0;\n        for (int i = 1; i <= 3; i++) {\n            float s = float(i) * 0.65;\n            occ += max(0.0, s - scene(p + geometricNormal * s).x / 0.4) / exp2(float(i));\n        }\n        ao = clamp(1.0 - occ * 0.45, 0.3, 1.0);\n    }\n    ao*=mix(1.0-0.4*crevice,1.0,max(snow,moss));\n    float sh = shadowAt(p + geometricNormal * 0.09, sd);\n    float diffuse = max(dot(n, sd), 0.0) * sh * (1.0-0.22*crevice*(1.0-snow));\n    vec3 ambient = vec3(0.25, 0.38, 0.53) * (0.38 + 0.62 * max(n.y, 0.0)) * ao;\n    ambient += vec3(0.13, 0.17, 0.12) * max(0.0, -n.y + 0.25);\n    vec3 col = stone * (ambient + vec3(2.2, 1.30, 0.65) * diffuse);\n    float stream = (1.0 - smoothstep(0.28, 0.72, abs(p.x - riverPath(p.z)))) * smoothstep(52.0, 58.0, p.z) * (1.0 - smoothstep(88.0, 100.0, p.z));\n    if (material < 1.5 && p.y < 22.0) {\n        float streak = noise2(vec2(p.x * 15.0, p.y * 2.0 + TIME * 2.5));\n        vec3 cascade = mix(vec3(0.17, 0.38, 0.42), vec3(0.68, 0.79, 0.77), streak * 0.8 + 0.2);\n        col = mix(col, cascade * (0.65 + 0.35 * sh), stream);\n    }\n    vec3 halfDir = normalize(sd - rd);\n    float spec = pow(max(dot(n, halfDir), 0.0), mix(18.0, 85.0, wet));\n    col += vec3(1.1, 0.9, 0.64) * spec * sh * (0.009 + 0.4 * wet + 0.08 * snow);\n    return col;\n}\n\nfloat wave(vec2 p) {\n    float time = TIME * waterMotion;\n    float v = sin(p.x * 1.9 + p.y * 0.7 - time * 1.4) * 0.35;\n    v += sin(p.x * -0.8 + p.y * 2.6 + time * 1.7) * 0.20;\n    v += sin(p.x * 4.8 + p.y * 3.1 - time * 2.1) * 0.04;\n    v += noise2(p * 6.0 + vec2(time * 0.5, 0.0)) * 0.08;\n    return v;\n}\n\nvec3 waterNormal(vec3 p, float t) {\n    float scale = mix(0.025, 0.006, smoothstep(10.0, 110.0, t));\n    float w = wave(p.xz);\n    return normalize(vec3((w - wave(p.xz + vec2(0.06, 0.0))) * scale / 0.06,\n                           1.0, (w - wave(p.xz + vec2(0.0, 0.06))) * scale / 0.06));\n}\n\nvec3 water(vec3 p, vec3 ro, vec3 rd, float t, vec3 n, vec3 reflection) {\n    float w = wave(p.xz);\n    float depth = max(0.0, -terrain(p.xz));\n    vec3 submerged = mix(vec3(0.12, 0.23, 0.16), vec3(0.007, 0.085, 0.095), 1.0 - exp(-depth * 0.65));\n    float caustic = pow(max(0.0, 0.5 + 0.5 * sin(p.x * 3.5 + w * 4.0) * sin(p.z * 4.1 - w * 5.0)), 5.0);\n    submerged += vec3(0.10, 0.18, 0.12) * caustic * exp(-depth * 0.8);\n    float fresnel = 0.035 + 0.965 * pow(1.0 - max(dot(-rd, n), 0.0), 5.0);\n    vec3 col = mix(submerged, reflection, fresnel);\n    vec3 sd = sunDirection();\n    float glitter = pow(max(dot(reflect(-sd, n), -rd), 0.0), 350.0);\n    col += vec3(5.0, 3.7, 1.9) * glitter;\n    float shore = min(depth, max(arch(p) * 2.0, 0.0));\n    float foam = (1.0 - smoothstep(0.08, 0.42, shore)) * smoothstep(0.2, 0.68, noise2(p.xz * 9.0 + w));\n    col = mix(col, vec3(0.55, 0.68, 0.65), foam * 0.6);\n    if(showBoat) {\n        vec3 q=boatSpace(p);\n        float behind=-q.x;\n        float spread=0.12+0.12*behind;\n        float wake=exp(-abs(abs(q.z)-spread)*65.0)*smoothstep(0.3,0.65,behind)*(1.0-smoothstep(1.0,2.7,behind));\n        col=mix(col,vec3(0.37,0.57,0.54),wake*0.28);\n    }\n    return haze(col, ro, rd, t);\n}\n\nvec3 tonemap(vec3 x) {\n    x *= exposure;\n    return clamp((x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14), 0.0, 1.0);\n}\n\nfloat birds(vec3 ro, vec3 rd, float distanceLimit) {\n    float flock = 0.0;\n    for(int i=0;i<5;i++) {\n        float fi = float(i);\n        vec3 center = vec3(8.0 + fi * 1.6 + 3.0 * sin(TIME * 0.12),\n                           25.0 + sin(fi * 1.9) * 1.3 + sin(TIME * 0.22) * 0.6,\n                           54.0 + fi * 2.0);\n        float t = (center.z - ro.z) / max(rd.z,0.01);\n        if(t > 0.0 && t < distanceLimit) {\n            vec2 q = (ro + rd * t - center).xy;\n            float wing = sin(TIME * 3.5 + fi * 1.7) * 0.38;\n            float curve = abs(q.y - abs(q.x) * wing - 0.06 * pow(abs(q.x) / 0.38,2.0));\n            float pixel = t / RENDERSIZE.y * 0.55;\n            float silhouette = (1.0-smoothstep(0.015,0.015+pixel,curve)) * (1.0-smoothstep(0.28,0.4,abs(q.x)));\n            flock = max(flock,silhouette);\n        }\n    }\n    return flock;\n}\n\nvoid main() {\n    if (PASSINDEX == 0) {\n        vec4 prior = IMG_THIS_PIXEL(geologyNoise);\n        if (prior.a > 0.5) { gl_FragColor = prior; return; }\n        // A padded, 16-cubed noise lattice generated entirely by this shader.\n        // Explicit interpolation keeps terrain derivatives smooth across GPUs.\n        vec2 pixel = floor(gl_FragCoord.xy);\n        vec2 tile = floor(pixel / 18.0);\n        vec3 lattice = vec3(mod(mod(pixel,18.0) - 1.0,16.0), mod(tile.x + tile.y * 4.0,16.0));\n        float groundNoise = hash12(mod(pixel,64.0) - 32.0);\n        gl_FragColor = vec4(hash13(lattice),groundNoise,hash13(mod(lattice+vec3(0,0,1),16.0)),1.0);\n        return;\n    }\n    if (PASSINDEX == 1) {\n        vec4 prior = IMG_THIS_PIXEL(forestData);\n        if(prior.a > 0.5) { gl_FragColor=prior; return; }\n        vec2 pixel=floor(gl_FragCoord.xy);\n        if(pixel.x >= 128.0 || pixel.y >= 96.0) { gl_FragColor=vec4(0,0,0,1); return; }\n        vec2 cell=pixel-vec2(64.0,16.0);\n        float seed=hash12(cell+13.5);\n        vec2 center=(cell+0.5)*FOREST_SPACING + vec2(seed-0.5,hash12(cell+8.1)-0.5)*0.72;\n        float base=terrain(center);\n        vec2 slope=vec2(terrain(center+vec2(0.4,0))-base,terrain(center+vec2(0,0.4))-base)/0.4;\n        float grove=noise2(center*0.12);\n        float ht=0.65+0.75*seed;\n        float fertile=smoothstep(0.7,1.8,base)*(1.0-smoothstep(8.0,12.0,base));\n        fertile*=1.0-smoothstep(0.7,1.6,length(slope));\n        if(center.y < -5.0 || center.y > 102.0 || seed > fertile*0.95 || grove < 0.3) ht=0.0;\n        gl_FragColor=vec4(base,ht,seed,1);\n        return;\n    }\n    vec2 uv = (gl_FragCoord.xy - 0.5 * RENDERSIZE) / RENDERSIZE.y;\n    float clockTime = TIME * 0.075;\n    vec3 ro = vec3(1.2 + sin(clockTime) * 1.8 * cameraDrift,\n                   5.0 + sin(clockTime * 0.7) * 0.3 * cameraDrift, -23.0);\n    vec3 target = vec3(-1.0, 10.3, 35.0);\n    vec3 forward = normalize(target - ro);\n    vec3 right = normalize(cross(vec3(0,1,0), forward));\n    vec3 up = cross(forward, right);\n    vec3 rd = normalize(forward * 1.08 + right * uv.x + up * uv.y);\n    float waterT = rd.y < -0.001 ? -ro.y / rd.y : FAR;\n    vec3 rayOrigin = ro, rayDir = rd;\n    vec3 waterPoint = vec3(0.0), waterN = vec3(0,1,0);\n    vec2 hit = vec2(FAR, -1.0);\n    bool isWater = false;\n    float limit = min(waterT, FAR);\n    // One tracing call site and one material call site serve both ray segments.\n    for (int bounce = 0; bounce < 2; bounce++) {\n        hit = trace(rayOrigin, rayDir, limit, bounce == 1);\n        if (bounce == 0 && hit.y < 0.0 && waterT < FAR) {\n            isWater = true;\n            waterPoint = ro + rd * waterT;\n            waterN = waterNormal(waterPoint, waterT);\n            rayOrigin = waterPoint + vec3(0.0, 0.10, 0.0);\n            rayDir = reflect(rd, waterN);\n            rayDir.y = max(rayDir.y, 0.015);\n            rayDir = normalize(rayDir);\n            limit = 230.0;\n        } else break;\n    }\n    vec3 col = vec3(0.0);\n    if (hit.y > 0.0) {\n        vec3 p = rayOrigin + rayDir * hit.x;\n        col = shadeRock(p, rayDir, hit.x, hit.y, isWater);\n        col = haze(col, rayOrigin, rayDir, hit.x);\n    } else col = sky(rayDir, true);\n    if (isWater) col = water(waterPoint, ro, rd, waterT, waterN, col);\n    float occlusionDistance = isWater ? waterT : (hit.y > 0.0 ? hit.x : FAR);\n    col = mix(col, vec3(0.055,0.073,0.085), birds(ro,rd,occlusionDistance) * 0.8);\n    // Restrained optical bloom around the actual sun direction.\n    float glow = pow(max(dot(rd, sunDirection()), 0.0), 24.0);\n    col += vec3(0.14, 0.073, 0.025) * glow;\n    col = pow(tonemap(col), vec3(1.0 / 2.2));\n    vec2 screen = isf_FragNormCoord;\n    float vignette = pow(max(16.0 * screen.x * screen.y * (1.0-screen.x) * (1.0-screen.y), 0.0), 0.10);\n    col *= 0.82 + 0.18 * vignette;\n    col += (hash12(gl_FragCoord.xy) - 0.5) / 255.0;\n    gl_FragColor = vec4(col, 1.0);\n}\n",
    "generator": {
      "model": "gpt-6",
      "provider": "openai",
      "prompt": "Refine The Tidal Cathedral with human feedback: fix the lower-left artifacts, improve mountain textures and the flat or plastic-looking rock, and improve tree scale or add a clear scale cue. Preserve the original scene and modeling while giving the shader a second round of refinement.",
      "params": {
        "study": "V2, refined with human feedback; V1 remains the independent model-led baseline.",
        "passes": 3,
        "workflow": "Original GLSL revised in Codex; diagnostic renders, visual A/B comparison, and canonical glslop WebGL2 validation.",
        "poster_width": 512,
        "poster_height": 512,
        "seamless_loop": false,
        "default_inputs": {
          "exposure": 0.95,
          "showBoat": true,
          "sunHeight": 0.27,
          "atmosphere": 0.5,
          "cameraDrift": 0.65,
          "waterMotion": 0.6
        },
        "poster_time_seconds": 6,
        "external_texture_assets": false
      }
    }
  }
}