#version 330 core in vec2 vUV; in vec3 vWorldPos; out vec4 FragColor; uniform sampler2D uHeightTex; uniform float uMinV; uniform float uMaxV; uniform float uHeightScale; uniform vec2 uTexelSize; uniform vec2 uPlaneSize; uniform vec3 uCameraPos; uniform vec3 uLightDir; float saturate(float x) { return clamp(x, 0.0, 1.0); } float value01(float v) { return saturate((v - uMinV) / max(uMaxV - uMinV, 1e-6)); } vec3 colorRamp(float t) { t = saturate(t); vec3 c0 = vec3(0.10, 0.15, 0.85); vec3 c1 = vec3(0.05, 0.75, 0.95); vec3 c2 = vec3(0.10, 0.90, 0.35); vec3 c3 = vec3(0.95, 0.90, 0.20); vec3 c4 = vec3(0.95, 0.25, 0.12); if (t < 0.25) return mix(c0, c1, t / 0.25); if (t < 0.50) return mix(c1, c2, (t - 0.25) / 0.25); if (t < 0.75) return mix(c2, c3, (t - 0.50) / 0.25); return mix(c3, c4, (t - 0.75) / 0.25); } void main() { float vC = texture(uHeightTex, vUV).r; float t = value01(vC); vec3 base = colorRamp(t); float vL = texture(uHeightTex, vUV - vec2(uTexelSize.x, 0.0)).r; float vR = texture(uHeightTex, vUV + vec2(uTexelSize.x, 0.0)).r; float vD = texture(uHeightTex, vUV - vec2(0.0, uTexelSize.y)).r; float vU = texture(uHeightTex, vUV + vec2(0.0, uTexelSize.y)).r; float hL = value01(vL) * uHeightScale; float hR = value01(vR) * uHeightScale; float hD = value01(vD) * uHeightScale; float hU = value01(vU) * uHeightScale; float dx = max(1e-6, uPlaneSize.x * uTexelSize.x); float dy = max(1e-6, uPlaneSize.y * uTexelSize.y); float dHx = (hR - hL) / (2.0 * dx); float dHy = (hU - hD) / (2.0 * dy); vec3 N = normalize(vec3(-dHx, -dHy, 1.0)); vec3 L = normalize(uLightDir); vec3 V = normalize(uCameraPos - vWorldPos); float diff = saturate(dot(N, L)); float spec = pow(saturate(dot(reflect(-L, N), V)), 20.0); vec3 col = base * (0.50 + 0.50 * diff); col += vec3(1.0) * spec * 0.20; FragColor = vec4(clamp(col, 0.0, 1.0), 1.0); }