Skip to content

feat: seamless chunk boundaries via extended heightmap normals#12

Merged
maxfelker merged 3 commits into
mainfrom
feat/seamless-chunk-normals
Mar 1, 2026
Merged

feat: seamless chunk boundaries via extended heightmap normals#12
maxfelker merged 3 commits into
mainfrom
feat/seamless-chunk-normals

Conversation

@maxfelker

Copy link
Copy Markdown
Owner

Problem

Visible seams appeared at chunk boundaries due to incorrect lighting at chunk edges.

Root Cause

ComputeNormals used clamped sampling at boundary vertices:

sampleH := func(row, col int) float64 {
    row = max(0, min(resolution-1, row))  // clamps — wrong at edges!
    col = max(0, min(resolution-1, col))
    ...
}

At the right edge (col=128), the central difference sees hL = hR, giving zero horizontal gradient and a flat, incorrect normal. Heights are continuous across chunks (same world-space noise), but the lighting seam came from mismatched normals.

Solution: Extended Heightmap Normal Computation

Generate a (resolution+2)×(resolution+2) = 131×131 heightmap for each chunk that includes a 1-cell border sampled one spacing unit beyond the chunk boundary. Use this extended heightmap to compute normals — every edge vertex now has real cross-boundary gradient data, no clamping.

chunk (0,0) extended: [-4, 0, 4, ..., 508, 512, 516]  ← wx coordinates
                        ↑ border                  ↑ border
chunk (1,0) extended: [508, 512, 516, ..., 1020, 1024, 1028]
                        ↑

Right edge of chunk(0,0) normal = central_diff(h(508), h(516)) / (2×4)
Left edge of chunk(1,0) normal = central_diff(h(508), h(516)) / (2×4)

Identical — seam eliminated.

Changes

File Change
wasm/terrain/heightmap.go Add GenerateExtendedHeightmap(cx, cz, cfg)
wasm/terrain/normals.go Add ComputeNormalsFromExtended(extHm, res, size, scale)
wasm/main.go Use extended path in goGenerateChunk
wasm/terrain/heightmap_test.go Extended heightmap size + inner match + border tests
wasm/terrain/normals_test.go Cross-chunk consistency + unit length + size tests

Performance

~7% more noise evaluations per chunk (131²=17,161 vs 129²=16,641). No TypeScript API changes. No GPU buffer format changes.

maxfelker and others added 3 commits March 1, 2026 11:39
Uses a (resolution+2)×(resolution+2) extended heightmap to compute
per-vertex normals without edge clamping. Right edge of chunk(0,0) and
left edge of chunk(1,0) now produce matching normals, eliminating the
lighting seam at chunk boundaries.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Generates a (resolution+2)×(resolution+2) heightmap including a 1-cell
border sampled one spacing unit beyond the chunk boundary. Used by
ComputeNormalsFromExtended to compute correct cross-boundary normals
without edge clamping.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace ComputeNormals (clamped edge sampling) with:
  1. GenerateExtendedHeightmap — (res+2)×(res+2) heightmap with real
     noise values 1 cell beyond chunk boundaries
  2. ComputeNormalsFromExtended — uses extended grid for central
     differences, no clamping needed

Edge normals now use actual neighbor terrain heights instead of
repeating the boundary value, eliminating the lighting seam at
chunk boundaries.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@maxfelker maxfelker merged commit f074cdb into main Mar 1, 2026
1 check passed
@maxfelker maxfelker deleted the feat/seamless-chunk-normals branch March 1, 2026 20:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant