0.00
60.0 fps

Log in to post a comment.

#version 300 es
precision highp float;

uniform float iTime;
uniform vec2  iResolution;
const float waterLevel = 120.0;
out vec4 fragColor;
const float hillMax = 155.0;   // end of green‐hill band
const float rockMax = 160.0;   // end of grey‐rock band
const float fade    =   5.0;   // width of the smooth blends
/*–– hash, noise & fbm ––*/
float hash(vec2 p) {
    p = 50.0 * fract(p * 0.3183099 + vec2(0.71, 0.113));
    return fract(p.x * p.y * (p.x + p.y));
}
float noise(in vec2 p) {
    vec2 i = floor(p), f = fract(p);
    vec2 u = f*f*(3.0-2.0*f);
    return mix(
        mix(hash(i), hash(i+vec2(1,0)), u.x),
        mix(hash(i+vec2(0,1)), hash(i+vec2(1,1)), u.x),
        u.y
    );
}
float fbm(in vec2 p) {
    float a=0.5, t=0.0;
    for(int i=0;i<6;i++){
        t += a*noise(p);
        p *= 2.2;
        a *= 0.5;
    }
    return t;
}
float ridged(in vec2 p) {
    float sum=0., amp=1.;
    for(int i=0;i<5;i++){
        float n = noise(p);
        sum += amp * (1.0 - abs(2.0*n - 1.0));
        p   *= 2.3;
        amp *= 0.5;
    }
    return sum;
}

/*–– terrain height ––*/
float heightField(vec2 p) {
    vec2 q    = p*0.0025;// + vec2(iTime*0.01, 0.0);
    vec2 warp = vec2(fbm(q*6.0), fbm(q*6.0+7.0))*20.0;
    p += warp;
    float h = fbm(p*0.0035)*100.0;
    h += ridged(p*0.005)*60.0;
    h += fbm(p*0.02)*15.0;

 // ——— flattening around waterLevel ———
    const float S = 50.0;                    // how “wide” the smoothing band is
    float d = h - (waterLevel-15.0);                // distance above water
    // t goes 0→1 as you move from waterLevel to waterLevel+S
    float t = clamp( d/S, 0.0, 1.0 );
    // mix between dead-flat waterLevel (t=0) and your real height (t=1)
    h = mix( waterLevel, h, t );

    return h;
}

float waterPlaneSDF(vec3 pos) {
    return pos.z - waterLevel;
}

/*–– SDF, normal & AO ––*/
float terrainSDF(vec3 pos) { return pos.z - heightField(pos.xy); }
vec3 terrainNormal(vec3 p) {
    float e=0.5;
    vec2 o=vec2(e,0.);
    return normalize(vec3(
        heightField(p.xy+o.xy) - heightField(p.xy-o.xy),
        heightField(p.xy+o.yx) - heightField(p.xy-o.yx),
        2.0*e
    ));
}
float calcAO(vec3 p, vec3 n) {
    float occ=0., sc=1.;
    for(int i=1;i<=5;i++){
        float hr = float(i)*0.2525;
        float d  = terrainSDF(p + n*hr);
        occ += (hr - d)*sc;
        sc *= 0.7;
    }
    return clamp(1.0 - occ*0.2, 0.0, 1.0);
}

/*–– camera ––*/
void getCamera(out vec3 camPos, out vec3 camDir,
               out vec3 camRight, out vec3 camUp, in vec2 uv)
{
    float t = iTime*0.096;
    vec2 path = vec2(sin(t), 0.7*cos(t*0.8))*90.0 + vec2(t*50.0,0.);
    float g = heightField(path);
    camPos = vec3(path, g+30.0);
    vec3 tgt = vec3(path+vec2(45.0,0.), heightField(path+vec2(45.0,0.))+10.0);
    camDir   = normalize(tgt - camPos);
    camRight = normalize(cross(vec3(0,0,1), camDir));
    camUp    = cross(camDir, camRight);
    camDir   = normalize(camDir + uv.x*camRight*0.8 + uv.y*camUp*0.6);
}

/*–– sky, sun & clouds ––*/
vec3 sky(vec3 rd) {
    vec3 sunDir = normalize(vec3(0.2,0.4,0.9));
    float sd    = clamp(dot(rd,sunDir),0.,1.);
    float sun   = smoothstep(0.996,1.,sd);
    float t     = clamp(rd.z*0.5+0.5,0.,1.);
    vec3 col    = mix(vec3(0.8,0.85,0.98), vec3(0.15,0.35,0.75), t);
    vec2 cuv = rd.xy/rd.z*120.0 + vec2(iTime*15.0,0.);
    float c = fbm(cuv*0.0007);
    float cl = smoothstep(0.5,0.7,c)*smoothstep(-0.2,0.5,rd.z);
    col = mix(col, vec3(1.0), cl*0.6);
    col += vec3(1.3,1.1,0.9)*pow(sun,16.0);
    return col;
}
float softShadow(vec3 ro, vec3 rd) {
    float res=1., t=1.;
    for(int i=0;i<12;i++){
        float h = terrainSDF(ro + rd*t);
        res = min(res, 12.*h/t);
        t += clamp(h,0.3,5.);
        if(res<0.001||t>200.) break;
    }
    return clamp(res,0.,1.);
}

bool intersectHeightfield(in vec3 camP, in vec3 ray, out float tHit) {
    // initial guess: where ray would hit water plane
    float t = (waterLevel - camP.z) / ray.z;
    t = max(t, 0.0);

    // Newton iterations
    for (int i = 0; i < 5; i++) {
        vec2 posXY = camP.xy + ray.xy * t;
        float h    = heightField(posXY);
        float f    = (camP.z + ray.z * t) - h;
        // estimate dh/dt = dot(∇H, ray.xy)
        float e    = 0.01;
        float hX   = heightField(posXY + vec2(e,0.0));
        float hY   = heightField(posXY + vec2(0.0,e));
        vec2 gradH = vec2((hX - h)/e, (hY - h)/e);
        float df   = ray.z - dot(gradH, ray.xy);
        // avoid division by zero
        t -= f / max(df, 0.0001);
        if (abs(f) < 0.01) break;
    }

    if (t < 0.0 || t > 500.0) return false;
    tHit = t;
    return true;
}

void main() {
    // === pixelation: quantize to 2×2 blocks ===
    float pixelSize = 2.0;                                   // size of block
    vec2 coord = floor(gl_FragCoord.xy / pixelSize) * pixelSize;
    // now compute uv from the *quantized* coordinate
    vec2 uv = (coord * 2.0 - iResolution.xy) / iResolution.y;

    // === camera setup (unchanged) ===
    vec3 camP, camD, camR, camU;
    getCamera(camP, camD, camR, camU, uv);
    vec3 ray = camD;

    // === rest of your ray-marching & shading ===
    float t = 0.0;
    bool hit = false;
    bool hitWater = false;
    vec3 pos;
    for (int i = 0; i < 256*2; i++) {
        pos = camP + ray * t;
        float dTerrain = terrainSDF(pos);
        float dPlane   = waterPlaneSDF(pos);
        float d        = min(dTerrain, dPlane);
        if (d < 0.01) {
            hit = true;
            hitWater = (dPlane < dTerrain);
            break;
        }
        t += clamp(d * 0.5, 0.0512, 6.0);
        if (t > 512.0) break;
    }

    vec3 skyCol = sky(ray);
    vec3 col    = skyCol;

    if (hit) {
        if (hitWater) {
            // water shading (unchanged) …
            vec3 wpos = pos;
            float w = sin((wpos.x + wpos.y) * 0.01 + iTime * 1.5) * 1.5;
            vec3 waveNormal = normalize(vec3(-dFdx(w), -dFdy(w), 1.0));
            float fres  = pow(1.0 - dot(ray, waveNormal), 5.0);
            vec3 waterCol = vec3(0.0, 0.0, 0.8);
            col = mix(waterCol, skyCol, fres * 0.7 + 0.1);
        } else {
            // terrain shading (unchanged) …
            vec3 n      = terrainNormal(pos);
            vec3 sunDir = normalize(vec3(0.2,0.4,0.9));
            float diff  = clamp(dot(n,sunDir),0.,1.) * softShadow(pos + n*0.3, sunDir);
            float ao    = calcAO(pos,n);
            float h     = pos.z;
            float hillAmt = smoothstep(0.0, hillMax, h)
                          * (1.0 - smoothstep(hillMax, hillMax+fade, h));
            vec3 hillCol  = mix(
                vec3(0.15,0.55,0.15),
                vec3(0.05,0.45,0.05),
                fbm(pos.xy * 0.01)
            );
            float rockAmt = smoothstep(hillMax, rockMax, h)
                          * (1.0 - smoothstep(rockMax, rockMax+fade, h));
            vec3 rockCol  = vec3(0.45) + ridged(pos.xy * 0.02) * 0.2;
            float peakAmt = smoothstep(rockMax, rockMax+fade, h);
            vec3 peakCol  = vec3(1.0);
            float wsum = hillAmt + rockAmt + peakAmt + 1e-3;
            hillAmt /= wsum; rockAmt /= wsum; peakAmt /= wsum;
            vec3 baseCol = hillAmt*hillCol + rockAmt*rockCol + peakAmt*peakCol;
            col = baseCol * (0.3 + diff*1.2) * ao
                + pow(max(dot(reflect(-sunDir,n), ray),0.0), 64.0) * 0.3;
            float fog = smoothstep(0.0,1.0,t/700.0);
            col = mix(col, skyCol, fog);
        }
    }

    col = pow(col, vec3(1.0/2.2));
    fragColor = vec4(col,1.0);
}