Skip to content

브라우저에서 오픈 월드 만들기, 10부: 이음새 혼란과 코너 보스전

Oleg Sidorkin, Cinevva CTO 겸 공동 창업자

처음 오셨나요? 시리즈 가이드를 보세요. spike가 무엇인지 설명하고 모든 파트를 링크해 둡니다.

앞부분이 차근차근 진행되는 느낌이었다면, 이 챕터는 전투 같았습니다.

Spike 17부터 22까지는 우리의 코너 케이스 시대였습니다. 듀얼 LOD marching cubes, heightmap에서 MC로 넘어가는 경계 이음새, 세 개나 네 개의 LOD 레벨이 만나는 혼합 해상도 코너 chunk, GPU 이음새 생성, 그리고 폴백 모드 동작. 각 spike는 우리가 마주쳤거나 예상했던 특정 실패 시나리오를 다뤘습니다.

Spike 17은 두 개의 LOD 레벨이 동시에 활성화된 상태의 듀얼 marching cubes를 테스트했습니다. 어려운 점은 하나의 chunk의 이웃들이 면마다 서로 다른 해상도일 수 있다는 것이었습니다. Spike 16에서 만든 전이 셀 로직은 한 번에 한 면에 대해서는 잘 작동했지만, chunk가 여러 면에 전이 셀이 필요할 때는 정점 버퍼 관리가 복잡해졌습니다. 각 면의 전이 셀은 다른 면을 덮어쓰지 않으면서 생성되고 추가되어야 했습니다.

새 탭에서 Spike 19 열기 ↗ · 소스 보기

반복해서 등장한 첫 번째 악당은 와인딩 순서였습니다. 여러 번 우리는 토폴로지 문제라고 생각했지만 알고 보니 방향 문제였습니다. 백페이스 컬링이 유효한 이음새 삼각형을 먹어 치우고 있었는데, 와인딩이 메인 메시 기준으로 뒤집혀 있었기 때문입니다. 같은 근본 원인인데 카메라 각도에 따라 시각적 증상이 달랐습니다. 해결책은 전이 셀 방출 코드에서 일관된 와인딩 규칙을 강제하고, 양면 머티리얼 토글로 그것을 검증하는 것이었습니다.

두 번째 악당은 부분적인 정확성에서 오는 잘못된 확신이었습니다. 이음새가 한 카메라 각도에서는 완벽해 보이다가, 고해상도와 저해상도 chunk 사이에서 LOD 역할이 바뀌면 깨질 수 있었습니다. 전이 셀은 비대칭입니다. 고해상도 쪽과 저해상도 쪽을 서로 다르게 샘플링합니다. 어떤 구성에서 "어느 쪽이 고해상도인가" 로직을 거꾸로 하면, 카메라가 특정 위치로 움직일 때만 그 버그가 보입니다.

그다음에는 우리가 가장 좋아하는 복구 중 하나가 있었습니다. 우리는 heightmap 타일에서 이음새가 잘리는 아티팩트를 쫓으면서 전이 로직을 탓하고 있었습니다. 거기에 이틀을 태웠습니다. 진짜 범인은 오래된 오버드로였습니다. 이전 프레임의 고해상도 지오메트리가, chunk가 더 낮은 LOD로 다운스케일된 뒤에도 버퍼 꼬리에 그대로 남아 있었습니다. 드로 범위는 여전히 예전의 더 큰 정점 수로 설정되어 있었죠. compute shader의 atomic counter가 보고하는 활성 정점 수로 드로 범위를 잘라내자, 그 "정체불명의 이음새 문제"가 사라졌습니다.

이건 렌더링 버그가 종종 메싱 버그로 위장한다는 좋은 일깨움이었습니다. 지오메트리는 처음부터 끝까지 옳았습니다. 드로 콜이 유효한 데이터의 끝을 지나서까지 읽고 있었을 뿐입니다.

Spike 22에 이르러서는 하이브리드 폴백을 테스트하고 있었습니다. chunk가 특정 조건에서 marching cubes에서 heightmap 모드로 전환할 수 있는 방식인데, 예를 들어 chunk에 볼류메트릭 편집이 없고 카메라에서 충분히 멀리 떨어져 있을 때입니다. 이것은 전부 아니면 전무 정책보다 더 실용적인 길을 줬습니다. 근거리의 편집된 chunk는 볼류메트릭 자유를 위해 MC를 씁니다. 원거리의 편집되지 않은 chunk는 효율을 위해 heightmap을 씁니다.

새 탭에서 Spike 22 열기 ↗ · 소스 보기

이 챕터는 롤러코스터의 가파른 낙하였습니다. 짜증나면서도 동시에 생산적이었습니다. 개별 수정 다수는 작았고, 어떤 때는 비교 연산자 하나나 오프셋 하나를 바꾸는 한 줄이었습니다. 하지만 그것들이 만들어낸, LOD 전이와 버퍼 관리와 드로 범위가 어떻게 상호작용하는지에 대한 이해는 결코 작지 않았습니다.

11부에서는 혼란 뒤에 온 안정화 레이어를 다룹니다. 정책 기반 chunk 모드, 그리고 반응적인 버그 수정에서 명시적인 시스템 규칙으로의 전환입니다.

이 챕터에서 언급된 기술

듀얼 LOD marching cubes. marching cubes를 두 개의 해상도 레벨에서 동시에 돌리면서, 전이 셀로 경계를 꿰매는 것입니다. 어려운 점은 하나의 chunk의 이웃들이 면마다 서로 다른 해상도일 수 있어서, 면마다 독립적인 전이 셀 생성이 필요하다는 것입니다. 각 면의 전이 셀은 다른 면을 덮어쓰지 않으면서 정점 버퍼에 추가됩니다. atomic counter가 모든 면에 걸친 총 활성 정점 수를 추적합니다.

와인딩 순서(winding order). 각 삼각형 안의 정점 순서가 어느 쪽이 "앞" 면인지 결정합니다. 일관된 와인딩(보통 바깥에서 봤을 때 반시계 방향)은 백페이스 컬링에 필요합니다. 전이 셀이 삼각형을 방출할 때 와인딩은 메인 메시의 규칙과 일치해야 합니다. 거꾸로 하면 백페이스 컬링이 유효한 이음새 삼각형을 먹어 치우는데, 이는 특정 카메라 각도에서 표면이 사라진 것처럼 보입니다. 흔한 디버깅 기법은 머티리얼에서 side: THREE.DoubleSide를 토글해서, 아티팩트가 와인딩 문제인지 진짜 토폴로지 구멍인지 확인하는 것입니다.

Heightmap-to-MC fallback. 하이브리드 chunk 모드로, 멀거나 편집되지 않은 chunk는 heightmap 지형(저렴하고 평평한 표면)을 쓰고, 가깝거나 편집된 chunk는 marching cubes(볼류메트릭, 동굴 지원)를 씁니다. 폴백 결정은 카메라로부터의 거리와 chunk에 SDF 편집이 있는지에 달려 있습니다. heightmap chunk와 MC chunk 사이의 이음새는 자체 전이 지오메트리가 필요한데, Transvoxel과 비슷하지만 두 개의 LOD 레벨이 아니라 두 개의 서로 다른 표현을 잇는다는 점이 다릅니다. 하이브리드 heightmap + 볼류메트릭 오버레이를 보세요.

드로 범위와 atomic counter. GPU 주도 메시 생성에서는 compute shader가 정점을 버퍼에 쓰고, 몇 개의 정점이 방출되었는지 추적하기 위해 atomic counter를 증가시킵니다. 드로 콜은 버퍼 용량이 아니라 이 counter를 정점 수로 써야 합니다. 드로 범위가 활성 카운트로 잘리지 않으면, 이전 프레임의 오래된 정점들(여전히 버퍼 꼬리에 남아 있는)이 잔상 지오메트리를 만듭니다. 얇은 조각과 깜박이는 삼각형인데, 토폴로지 오류처럼 보이지만 사실은 유효한 데이터를 지나서 읽어서 생긴 렌더링 아티팩트입니다.


12부 중 10부.
이전: 9부 - Transvoxel은 스캐폴드로 시작했다
다음: 11부 - 하드코딩 모드가 아니라 정책 모드
시리즈 가이드: /ko/blog/2026-02-25-open-world-browser-series-guide