From faf1d70c34d0315e97e2b75ea886859fc6a2b54d Mon Sep 17 00:00:00 2001 From: Kelsi Date: Mon, 6 Apr 2026 18:18:14 -0700 Subject: [PATCH] fix(rendering): reduce terrain chunk edge seams (#56) Two sources of visible chunk-boundary squares: 1. Derivative-based bump mapping (bumpStrength=9) used dFdx/dFdy which are invalid across draw-call boundaries, producing strong normal discontinuities at every chunk edge. Fade bump to zero near chunk edges using LayerUV as the chunk-space distance metric. 2. sampleAlpha used an abrupt step() to switch between point-sampled and 4-tap-blurred alpha, creating a visible ring 2 texels from each chunk edge. Replace with smoothstep transition and a 5-tap average that includes the center sample. --- assets/shaders/terrain.frag.glsl | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/assets/shaders/terrain.frag.glsl b/assets/shaders/terrain.frag.glsl index 0fcdd7dd..1a0de9b1 100644 --- a/assets/shaders/terrain.frag.glsl +++ b/assets/shaders/terrain.frag.glsl @@ -50,19 +50,21 @@ float sampleShadowPCF(sampler2DShadow smap, vec3 coords) { } float sampleAlpha(sampler2D tex, vec2 uv) { + // Smooth 5-tap box near chunk edges to hide alpha-map seams; + // blends gradually to avoid a visible ring at the transition. vec2 edge = min(uv, 1.0 - uv); float border = min(edge.x, edge.y); - float doBlur = step(border, 2.0 / 64.0); - if (doBlur < 0.5) { - return texture(tex, uv).r; - } + float blurWeight = 1.0 - smoothstep(0.5 / 64.0, 3.0 / 64.0, border); + float center = texture(tex, uv).r; + if (blurWeight < 0.001) return center; vec2 texel = vec2(1.0 / 64.0); - float a = 0.0; - a += texture(tex, uv + vec2(-texel.x, 0.0)).r; - a += texture(tex, uv + vec2(texel.x, 0.0)).r; - a += texture(tex, uv + vec2(0.0, -texel.y)).r; - a += texture(tex, uv + vec2(0.0, texel.y)).r; - return a * 0.25; + float avg = center; + avg += texture(tex, uv + vec2(-texel.x, 0.0)).r; + avg += texture(tex, uv + vec2( texel.x, 0.0)).r; + avg += texture(tex, uv + vec2(0.0, -texel.y)).r; + avg += texture(tex, uv + vec2(0.0, texel.y)).r; + avg *= 0.2; + return mix(center, avg, blurWeight); } void main() { @@ -87,9 +89,12 @@ void main() { vec3 norm = normalize(Normal); // Derivative-based normal mapping: perturb vertex normal using texture detail. - // Fade out with distance — looks noisy/harsh beyond ~100 units. + // Fade out with distance and near chunk edges (dFdx/dFdy are invalid across + // chunk draw-call boundaries, producing visible seams if not faded). float fragDist = length(viewPos.xyz - FragPos); float bumpFade = 1.0 - smoothstep(50.0, 125.0, fragDist); + float edgeDist = min(min(LayerUV.x, 1.0 - LayerUV.x), min(LayerUV.y, 1.0 - LayerUV.y)); + bumpFade *= smoothstep(0.0, 0.06, edgeDist); if (bumpFade > 0.001) { float lum = dot(finalColor.rgb, vec3(0.299, 0.587, 0.114)); float dLdx = dFdx(lum);