Skip to content

Building an open world in the browser, part 10: Seam chaos and the corner boss fight

By Oleg Sidorkin, CTO and Co-Founder of Cinevva

New here? Use the series guide. It explains what a spike is and links all parts.

If the earlier parts felt methodical, this chapter felt like combat.

Spikes 17 through 22 were our corner-case era. Dual-LOD marching cubes, heightmap-to-MC boundary seams, mixed-resolution corner chunks where three or four LOD levels meet, GPU seam generation, and fallback mode behavior. Each spike addressed a specific failure scenario we'd encountered or anticipated.

Spike 17 tested dual marching cubes with two LOD levels active simultaneously. The challenge was that a single chunk's neighbors could be at different resolutions on different faces. The transition cell logic from Spike 16 worked for one face at a time, but when a chunk needed transition cells on multiple faces, the vertex buffer management got complicated. Each face's transition cells had to be generated and appended without overwriting the others.

Open Spike 19 in a new tab ↗ · View source

The first repeated villain was winding order. Several times we thought we had topology problems, then found orientation problems. Backface culling was eating valid seam triangles because the winding was flipped relative to the main mesh. Same root cause, different visual symptom depending on camera angle. The fix was enforcing a consistent winding convention in the transition cell emission code and verifying it with a double-sided material toggle.

The second villain was false confidence from partial correctness. A seam could look perfect from one camera angle and break when LOD roles swapped between higher and lower resolution chunks. The transition cell is asymmetric. It samples from the high-res side and the low-res side differently. If you get the "which side is high-res" logic backwards for one configuration, you only see the bug when the camera moves to a specific position.

Then came one of our favorite recoveries. We were chasing a seam cutoff artifact on heightmap tiles and blaming the transition logic. Burned two days on it. The real culprit was stale overdraw. Higher-resolution geometry from a previous frame was still living in the buffer tail after the chunk downscaled to a lower LOD. The draw range was still set to the old, larger vertex count. Once we clipped the draw range to the active vertex count reported by the compute shader's atomic counter, the "mystery seam issue" disappeared.

That was a great reminder that rendering bugs often masquerade as meshing bugs. The geometry was correct the whole time. The draw call was just reading past the end of valid data.

By Spike 22 we were testing hybrid fallback where chunks could switch from marching cubes to heightmap mode under specific conditions, like when the chunk contains no volumetric edits and sits far enough from the camera. This gave us a more practical path than an all-or-nothing policy. Near-field edited chunks use MC for volumetric freedom. Far-field unedited chunks use heightmaps for efficiency.

Open Spike 22 in a new tab ↗ · View source

This chapter was the steep drop of the roller coaster. Frustrating and productive at the same time. Many of the individual fixes were small, sometimes a single line changing a comparison operator or an offset. But the understanding they produced about how LOD transitions, buffer management, and draw ranges interact wasn't small at all.

In part 11 we cover the stabilization layer that came after the chaos: policy-based chunk modes and the transition from reactive bug-fixing to explicit system rules.

Technology referenced in this chapter

Dual-LOD marching cubes. Running marching cubes at two resolution levels simultaneously, with transition cells stitching the boundary. The challenge is that a single chunk's neighbors can be at different resolutions on different faces, requiring independent transition cell generation per face. Each face's transition cells are appended to the vertex buffer without overwriting the others. Atomic counters track the total active vertex count across all faces.

Winding order. The vertex order within each triangle determines which side is the "front" face. Consistent winding (typically counter-clockwise when viewed from outside) is required for backface culling. When transition cells emit triangles, the winding must match the main mesh's convention. Getting it backwards causes backface culling to eat valid seam triangles, which looks like missing surfaces from certain camera angles. A common debugging technique is toggling side: THREE.DoubleSide on the material to confirm whether artifacts are winding issues or genuine topology gaps.

Heightmap-to-MC fallback. A hybrid chunk mode where far or unedited chunks use heightmap terrain (cheap, flat surface) while near or edited chunks use marching cubes (volumetric, supports caves). The fallback decision depends on distance from camera and whether the chunk contains SDF edits. The seam between a heightmap chunk and an MC chunk requires its own transition geometry, similar to Transvoxel but bridging two different representations rather than two LOD levels. See hybrid heightmap + volumetric overlays.

Draw range and atomic counters. In GPU-driven mesh generation, the compute shader writes vertices into a buffer and increments an atomic counter to track how many vertices were emitted. The draw call must use this counter as the vertex count, not the buffer capacity. If the draw range isn't clipped to the active count, stale vertices from previous frames (still living in the buffer tail) produce ghost geometry: thin slivers and flickering triangles that look like topology errors but are actually rendering artifacts from reading past valid data.


Part 10 of 12.
Previous: Part 9 - Transvoxel started with a scaffold
Next: Part 11 - Policy mode, not hardcoded mode
Series guide: /blog/2026-02-25-open-world-browser-series-guide