#version 450 core out gl_PerVertex { vec4 gl_Position; }; layout(std140, binding=0) uniform PerFrame { mat4 uView; mat4 uProj; vec4 uLightDir; }; layout(std140, binding=1) uniform PerObject { mat4 uModel; }; layout(location=0) in vec3 aPos; // XY from grid, Z ignored layout(location=2) in vec2 aUV; // 0..1 across the grid layout(location=0) out VS_OUT { vec3 wPos; vec2 uv; } v; // ---- tiny fBm noise (tile-free, cheap) ---- float hash(vec2 p){ return fract(sin(dot(p, vec2(127.1,311.7))) * 43758.5453); } float noise(vec2 p){ vec2 i=floor(p), f=fract(p); float a = hash(i + vec2(0,0)); float b = hash(i + vec2(1,0)); float c = hash(i + vec2(0,1)); float d = hash(i + vec2(1,1)); 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 a=0.5, s=0.0; for(int i=0;i<5;i++){ s += a*noise(p); p = p*2.02 + 17.0; a*=0.5; } return s; } // tunables uniform float uHeightScale = 3.0; // world Z amplitude uniform float uFreq = 2.0; // base frequency (cycles over 0..1 uv) void main(){ float h = fbm(aUV * uFreq) * uHeightScale; // Z-up displacement vec3 p = vec3(aPos.xy, h); vec4 w = uModel * vec4(p,1.0); v.wPos = w.xyz; v.uv = aUV; gl_Position = uProj * uView * w; }