From 163077fef008a9ba3cd19bfb92c5f980502bbfcc Mon Sep 17 00:00:00 2001 From: Kelsi Date: Fri, 8 May 2026 12:34:16 -0700 Subject: [PATCH] =?UTF-8?q?fix(terrain):=20hide=20chunk=20grid=20by=20tili?= =?UTF-8?q?ng=20textures=204=C3=97=20per=20chunk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two changes that work together: 1. terrain_mesh.cpp: bump texture-coord scale from 1× to 4× per chunk so the texture's own pattern repeats every ~8 yards instead of every ~33 yards. At 1×, the texture's repeat frequency syncs with the chunk grid and any per-chunk alpha difference reads as a hard 33-yard square. At 4× the pattern noise breaks up the boundary line and the eye stops locking onto the grid. 2. terrain.frag.glsl: widen the alpha-edge feather from 3 to 8 texels and use 9 taps instead of 5 so per-chunk alpha values bleed across the chunk boundary instead of stepping. Hard alpha steps were the second contributor to visible chunk tiles in painted regions. Reported by user via screenshot showing obvious chunk-grid artifacts in painted areas of the texture-paint editor. --- assets/shaders/terrain.frag.glsl | 13 ++++++++++--- src/pipeline/terrain_mesh.cpp | 8 ++++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/assets/shaders/terrain.frag.glsl b/assets/shaders/terrain.frag.glsl index 1a0de9b1..ab2d7cbe 100644 --- a/assets/shaders/terrain.frag.glsl +++ b/assets/shaders/terrain.frag.glsl @@ -50,11 +50,14 @@ float sampleShadowPCF(sampler2DShadow smap, vec3 coords) { } float sampleAlpha(sampler2D tex, vec2 uv) { - // Smooth 5-tap box near chunk edges to hide alpha-map seams; + // Smooth 9-tap box near chunk edges to hide alpha-map seams; // blends gradually to avoid a visible ring at the transition. + // Wider feather (8 texels) makes per-chunk alpha differences + // bleed across the boundary so the chunk grid stops reading + // as a hard step. vec2 edge = min(uv, 1.0 - uv); float border = min(edge.x, edge.y); - float blurWeight = 1.0 - smoothstep(0.5 / 64.0, 3.0 / 64.0, border); + float blurWeight = 1.0 - smoothstep(1.0 / 64.0, 8.0 / 64.0, border); float center = texture(tex, uv).r; if (blurWeight < 0.001) return center; vec2 texel = vec2(1.0 / 64.0); @@ -63,7 +66,11 @@ float sampleAlpha(sampler2D tex, vec2 uv) { 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; + avg += texture(tex, uv + vec2(-texel.x, -texel.y)).r; + avg += texture(tex, uv + vec2( texel.x, -texel.y)).r; + avg += texture(tex, uv + vec2(-texel.x, texel.y)).r; + avg += texture(tex, uv + vec2( texel.x, texel.y)).r; + avg *= 1.0 / 9.0; return mix(center, avg, blurWeight); } diff --git a/src/pipeline/terrain_mesh.cpp b/src/pipeline/terrain_mesh.cpp index cf95335b..11bc90a2 100644 --- a/src/pipeline/terrain_mesh.cpp +++ b/src/pipeline/terrain_mesh.cpp @@ -237,8 +237,12 @@ std::vector TerrainMeshGenerator::generateVertices(const MapChunk } // Texture coordinates: world-aligned so patterns don't reset per chunk. - // Keep one texture repeat per chunk (matches prior scale). - constexpr float texScale = 1.0f / CHUNK_SIZE; + // Tile each texture 4× per chunk (one repeat every ~8 yards) so the + // texture's own pattern noise breaks up the chunk grid rather than + // syncing with it. At 1 repeat/chunk the per-chunk alpha differences + // read as obvious 33-yard squares; at 4× the pattern is small enough + // that the eye no longer locks onto the chunk boundary. + constexpr float texScale = 4.0f / CHUNK_SIZE; vertex.texCoord[0] = -vertex.position[1] * texScale; vertex.texCoord[1] = -vertex.position[0] * texScale;