cool
Log in to post a comment.
#version 300 es
precision highp float;
uniform float iTime;
uniform vec2 iResolution;
out vec4 fragColor;
// Hash & noise
float hash(vec2 p) {
p = fract(p * vec2(123.4, 456.7));
p += dot(p, p + 34.56);
return fract(p.x * p.y);
}
float noise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
float a = hash(i);
float b = hash(i + vec2(1.0, 0.0));
float c = hash(i + vec2(0.0, 1.0));
float d = hash(i + vec2(1.0, 1.0));
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
}
float fbm(vec2 p) {
float t = 0.0;
float amp = 0.5;
for (int i = 0; i < 8; i++) {
t += noise(p) * amp;
p *= 2.0;
amp *= 0.5;
}
return t;
}
float getHeight(vec2 p) {
float base = fbm(p * 0.6); // medium-scale terrain
float fine = fbm(p * 2.5) * 0.15; // small bumps
float height = base * 3.0 + fine * 3.0;
// Smooth lowlands: fade out detail if base is low
float lowland = smoothstep(0.0, 0.6, height);
return mix(base * 3.0, height, lowland);
}
vec3 getNormal(vec2 p) {
float eps = 0.1;
float h = getHeight(p);
float hx = getHeight(p + vec2(eps, 0.0));
float hy = getHeight(p + vec2(0.0, eps));
return normalize(vec3(- (hx - h), - (hy - h), 1.0));
}
vec3 terrainColor(float height) {
if (height > 2.3) return vec3(1.0); // snow
else if (height > 1.6) return vec3(0.5, 0.5, 0.5); // rock
else return vec3(0.1, 0.5, 0.2); // grass
}
// Sky-projected clouds (directional)
vec3 skyWithClouds(vec3 rayDir, float time) {
float elevation = rayDir.z;
// Project rayDir to a 2D domain for clouds
vec2 cloudUV = normalize(rayDir.xy) * 0.5 + vec2(0.5); // sphere to [0,1]
cloudUV += vec2(time * 0.01, 0.0); // move clouds
float cloud = fbm(cloudUV * 4.0); // denser clouds
float clouds = smoothstep(0.5, 0.7, cloud);
vec3 skyBase = vec3(0.6, 0.8, 1.0);
vec3 cloudColor = mix(skyBase, vec3(1.0), clouds * smoothstep(0.0, 0.5, elevation));
return cloudColor;
}
// Simulate forward movement with heading drift
vec2 getCameraPath(float time) {
vec2 pos = vec2(0.0);
float angle = 0.0;
// Simulate small steering changes over time
for (float t = 0.0; t < time; t += 0.1) {
angle = 0.3 * sin(t * 0.07) + 0.15 * sin(t * 0.17); // same curve
vec2 dir = vec2(cos(angle), sin(angle));
pos += dir * 0.04; // speed * dt
}
return pos;
}
void main() {
vec2 uv = (gl_FragCoord.xy / iResolution.xy) * 2.0 - 1.0;
uv.x *= iResolution.x / iResolution.y;
// ✨ Smoothly changing heading angle
float headingAngle = 0.3 * sin(iTime * 0.07) + 0.15 * sin(iTime * 0.17);
vec2 forwardDir = vec2(cos(headingAngle), sin(headingAngle));
vec2 camPath = getCameraPath(iTime); // ✨ use integrated position
// ✨ Always moving forward in current heading
float speed = 0.4;
vec3 camPos = vec3(camPath, 2.0); // eye height
vec3 targetPos = vec3(camPath + forwardDir * 5.0, 1.4); // look slightly down
vec3 camDir = normalize(targetPos - camPos);
vec3 camRight = normalize(cross(vec3(0.0, 0.0, 1.0), camDir));
vec3 camUp = cross(camDir, camRight);
vec3 rayDir = normalize(camDir + uv.x * camRight + uv.y * camUp);
vec3 rayPos = camPos;
// Raymarch loop
float t = 0.0;
float maxDist = 100.0;
bool hit = false;
vec3 hitPos;
float dist = 0.0;
for (int i = 0; i < 64; i++) {
vec3 pos = camPos + rayDir * t;
float h = getHeight(pos.xy);
dist = pos.z - h;
if (dist < 0.0) {
hit = true;
hitPos = pos;
break;
}
t += clamp(dist * 0.5, 0.01, 0.2); // smart step size
if (t > maxDist) break;
}
vec3 color;
if (hit) {
float height = getHeight(hitPos.xy);
bool isLake = height < 0.5;
if (isLake) height = 0.5; // flatten lake
vec3 baseColor = isLake
? vec3(0.1, 0.3, 0.6) // lake blue
: terrainColor(height);
vec3 normal = getNormal(hitPos.xy);
vec3 lightDir = normalize(vec3(0.4, 0.5, 1.0));
float diff = max(dot(normal, lightDir), 0.0);
float spec = pow(max(dot(normal, normalize(lightDir + vec3(0.0, 0.0, 1.0))), 0.0), 64.0);
vec3 surfaceColor = baseColor * diff + vec3(1.0) * spec * 0.3;
float fogAmount = smoothstep(30.0, 80.0, t);
vec3 skyColor = skyWithClouds(rayDir, iTime);
color = mix(surfaceColor, skyColor, fogAmount);
} else {
// Ray didn't hit terrain — render sky with clouds
color = skyWithClouds(rayDir, iTime);
}
fragColor = vec4(color, 1.0);
}