Simple height map
Log in to post a comment.
#version 300 es precision highp float; // Forked from "Speed tracer" by reinder // https://oneshader.net/shader/9708b71579 uniform float iTime; uniform vec2 iResolution; out vec4 fragColor; //––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– // 1) 2D noise & FBM for heightmap //––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– float hash(vec2 p){ return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123); } 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(a,b,u.x) + (c - a)*u.y*(1.0 - u.x) + (d - b)*u.x*u.y; } float fbm(vec2 p){ float v = 0.0, amp = 0.5; for(int i=0; i<15; i++){ v += amp * noise(p); p *= 2.0; amp *= 0.5; } return v; } // Sample integer block height at (x,z) int getHeight(ivec2 cellXZ){ // 1) sample raw fbm in [0,1] float raw = fbm(vec2(cellXZ) * 0.1); // 2) remap to [-1,1] float norm = raw * 2.0 - 1.0; // 3) exaggerate extremes (peaks ↑, valleys ↓) // change 1.5 to control the “steepness” of the exaggeration float ex = sign(norm) * pow(abs(norm), 1.5); // 4) remap back to [0,1] float mapped = ex * 0.5 + 0.5; // 5) stretch mean-zero deviation by 12 units and shift down 2 units // → peaks up around +10, valleys down around −2 float h = mapped * 15.0 - 2.0; // 6) clamp so you don’t carve below y=0 h = max(h, 0.0); return int(floor(h)); } //––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– // 2) Voxel DDA ray‐march //––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– vec3 rayVoxelMarch(vec3 ro, vec3 rd, out ivec3 hitCell, out vec3 hitNormal){ // starting cell ivec3 cell = ivec3(floor(ro)); // ray steps per axis vec3 invDir = 1.0 / rd; ivec3 step = ivec3(sign(rd)); // t to first boundary vec3 tDelta = abs(invDir); float tx = ((step.x>0?float(cell.x)+1.0:float(cell.x)) - ro.x) * invDir.x; float ty = ((step.y>0?float(cell.y)+1.0:float(cell.y)) - ro.y) * invDir.y; float tz = ((step.z>0?float(cell.z)+1.0:float(cell.z)) - ro.z) * invDir.z; float t = 0.0; vec3 normal = vec3(0.0); // march up to N steps for(int i=0; i<250; i++){ // check terrain height int h = getHeight(ivec2(cell.x, cell.z)); if(cell.y <= h){ hitCell = cell; hitNormal = normal; return ro + rd * t; } // advance to next cell boundary if(tx < ty){ if(tx < tz){ cell.x += step.x; t = tx; tx += tDelta.x; normal = vec3(-float(step.x), 0.0, 0.0); } else { cell.z += step.z; t = tz; tz += tDelta.z; normal = vec3(0.0, 0.0, -float(step.z)); } } else { if(ty < tz){ cell.y += step.y; t = ty; ty += tDelta.y; normal = vec3(0.0, -float(step.y), 0.0); } else { cell.z += step.z; t = tz; tz += tDelta.z; normal = vec3(0.0, 0.0, -float(step.z)); } } if(t > 100.0) break; } // no hit → return far point hitCell = ivec3(0); hitNormal = vec3(0.0); return ro + rd * 100.0; } //––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– // 3) Simple lighting & sky //––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– vec3 skyColor(vec3 rd){ float t = clamp(rd.y * 0.25 + 0.5, 0.0, 1.0); return mix(vec3(0.6,0.7,0.8), vec3(0.8,0.9,1.0), t); } vec3 shadeBlock(ivec3 cell, vec3 normal){ // Color by block type int h = getHeight(ivec2(cell.x, cell.z)); vec3 base = (cell.y == h) ? vec3(0.2,0.8,0.2) // grass : vec3(0.6); // stone // directional light vec3 L = normalize(vec3(0.5,1.0,0.3)); float dif = max(dot(normal, L), 0.0); return base * (0.2 + 0.8 * dif); } //––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– // 4) Main //––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– void main(){ // 1) screen → NDC, maintain aspect vec2 uv = (gl_FragCoord.xy / iResolution.xy) * 2.0 - 1.0; uv.x *= iResolution.x / iResolution.y; // 2) Camera motion parameters float orbitSpeed = 0.2; float orbitRadius = 30.0; float bobSpeed = 0.5; float bobHeight = 5.0; float minCamY = 8.0; // clamp floor // 3) Compute orbit + bob, then clamp float angle = iTime * orbitSpeed; float rawY = 10.0 + sin(iTime * bobSpeed) * bobHeight; float camY = max(rawY, minCamY); // never go below minCamY vec3 ro = vec3( cos(angle) * orbitRadius, camY, sin(angle) * orbitRadius ); // 4) Moving look-at target vec3 target = vec3(0.0, 0.0, iTime * 3.0); // 5) Build camera basis vec3 forward = normalize(target - ro); vec3 worldUp = vec3(0.0, 1.0, 0.0); vec3 right = normalize(cross(forward, worldUp)); vec3 up = cross(right, forward); // 6) Ray direction vec3 rd = normalize( forward + uv.x * right + (uv.y * 0.5) * up ); // 7) Voxel march & shading ivec3 hitCell; vec3 hitNormal; vec3 hitPos = rayVoxelMarch(ro, rd, hitCell, hitNormal); vec3 col = (length(hitNormal) > 0.0) ? shadeBlock(hitCell, hitNormal) : skyColor(rd); fragColor = vec4(col, 1.0); }