cool
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);
}