Skip to content

브라우저에서 오픈 월드를 만들려면 실제로 무엇이 필요한가

Mariana Muntean, Cinevva CEO

The open world running in a browser at 120 FPS — terrain, trees, physics, and a player capsule, all rendered in a single tab

Cinevva 팀은 최근 게임 개발 분야에서 보기 드물게 투명한 엔지니어링 일지 중 하나를 공개했습니다. 멀티플레이어 오픈 월드를 전적으로 브라우저 안에서 돌리려는 우리의 시도를 기록한 12부작 시리즈입니다. 다운로드도 없고, 앱스토어도 없습니다. 그냥 URL 하나면 됩니다.

이 프로젝트는 우리가 "spike"라고 부르는 24개의 기술 실험으로 이어졌습니다. 각각은 위험한 질문 하나에 답하도록 설계된 짧고 집중적인 프로토타입입니다. 모든 spike는 지금 당장 브라우저에서 열어 실행할 수 있는 실제 소스 코드와 함께 공개되었습니다. 이 시리즈는 Cinevva의 CTO이자 공동 창업자인 Oleg Sidorkin이 썼고, 마케팅 글이라기보다 2026년의 브라우저가 실제로 무엇을 할 수 있는지를 최전선에서 적은 현장 일지에 가깝습니다.

이 시리즈가 읽을 가치가 있는 이유는, 설령 지형 시스템을 만들 계획이 전혀 없더라도, 그 밑에 깔린 방법에 있습니다. 비싼 데 발을 들이기 전에 야심 찬 프로젝트의 위험을 어떻게 줄일 수 있는지 보여 주는 사례 연구입니다.

가장 어려운 질문부터 시작하기

대부분의 오픈 월드 프로젝트는 예측 가능한 순서로 죽습니다. 먼저 멋진 콘셉트가 나옵니다. 그다음 예쁜 장면이 나옵니다. 그러고 나서 게임플레이가 존재하기도 전에 프레임 예산이 이미 다 소진됐다는 걸 발견합니다.

우리 팀은 순서를 뒤집었습니다. 첫 번째 spike는 일부러 투박하게 만들었습니다. 512미터 지형 메시, 인스턴스화된 오브젝트 500개, 절차적 높이 노이즈, 물 평면, 그리고 안개. 그림자도 없고 미술 패스도 없었습니다. 유일한 질문은 카메라가 그 안을 이동하는 동안 브라우저가 안정적인 프레임 레이트를 유지할 수 있는가였습니다.

가능했습니다. 그리고 그 "예"는 Oleg가 "베이스라인 계약"이라고 부르는 것을 확립했습니다. 최소한의 장면에 대한 측정된 기준 비용으로, 이후의 모든 기능은 이에 비추어 스스로를 정당화해야 했습니다. 새로운 효과가 멋져 보여도 프레임 예산을 터뜨리면, 그건 출시되지 않았습니다. 적어도 아직은 아니었죠.

이런 종류의 규율은 당연한 소리처럼 들립니다. 하지만 실제로는, 모두가 다음 시각적 성과에 들떠 있는 빠르게 움직이는 프로토타입 환경에서는 드뭅니다.

물리라는 도박

두 번째 실험은 브라우저 게임 개발자들 사이에서 의견이 갈리는 아키텍처 논쟁을 다뤘습니다. 물리는 더 간단한 메인 스레드에서 돌려야 할까요, 아니면 렌더링을 막지 않는 Web Worker에서 돌려야 할까요?

Worker 기반 물리는 서류상으로는 더 깔끔합니다. 실제로 두려운 건 지연입니다. 모든 입력 이벤트는 메시지 경계를 두 번 넘어야 합니다. 한 번은 Worker에 도달하기 위해, 한 번은 결과를 가져오기 위해. 그 왕복이 너무 느리면, 키를 누르고 캐릭터가 움직이는 걸 보는 것이 굼뜨게 느껴질 겁니다.

팀은 Rapier 물리 엔진(Rust에서 WebAssembly로 컴파일됨)을 전용 Worker에 통합하고, 메시지 파이프라인을 연결한 뒤 측정했습니다. 오버헤드는 무시할 만한 수준이었습니다. 조작은 여전히 즉각적으로 느껴졌습니다. 하지만 우리는 보편적인 규칙이 아니라 특정한 시나리오 하나를 검증한 것이라는 점을 신중히 기록해 두었습니다. 나중에 GPU 부하와 스트리밍 복잡도가 바뀌면, 그 가정들은 다시 점검해야 할 터였죠.

프로젝트를 구한 따분한 spike들

시리즈의 3부에는 스크린샷이 하나도 없습니다. 화려해 보이지 않지만 제품 수준의 결과를 안고 있던 세 가지 실험을 다룹니다.

첫 번째는 Cloudflare Durable Objects가 게임 수준의 틱 레이트로 실시간 위치 브로드캐스트를 처리할 수 있는지 시험했습니다. 멀티플레이어의 근간이죠. 이것이 실패했다면, 전체 네트워크 아키텍처는 단일 아일랜드 소유 방식 대신 일찌감치 샤딩이 필요했을 겁니다.

두 번째는 모바일 품질 프로파일을 검증했습니다. 이름만 바꾼 데스크톱 프리셋이 아니라, 같은 지형 베이스라인에서 나온 명시적인 저비용 렌더링 경로였습니다. 질문은 렌더러를 다시 쓰지 않고도 모바일 GPU 제약 아래에서 월드가 읽기 좋고 반응성 있게 유지될 수 있는가였습니다.

세 번째는 크리에이터 워크플로를 위한 AI 생성 동작 스크립트가 프로덕션에 쓸 만큼 신뢰할 수 있는지 평가했습니다.

이 중 어느 것도 데모 영상을 만들어내지 않았습니다. 셋 다 이후의 모든 아키텍처 결정을 형성한 단단한 경계를 세웠습니다. Oleg는 이 "화려하지 않은 spike들이 시각적 spike들보다 더 빠르게 아키텍처를 바꿨다"고 씁니다.

스트리밍: 예쁜 프로젝트가 무너지는 지점

Spike 6: Chunk streaming in action — each colored area is a terrain chunk that loads and unloads dynamically as the camera moves

정지된 한 프레임에는 많은 것을 숨길 수 있습니다. 하지만 달리는 도중 청크 경계를 넘을 때 생기는 40밀리초의 끊김은 숨길 수 없습니다.

팀은 고급 지형을 만들기 전에 스트리밍을 시험하며 관심사를 의도적으로 분리했습니다. Spike 6은 단순한 콘텐츠로 이웃 청크 로딩을 검증했습니다. 그 깨끗한 신호를 얻은 뒤에야 Spike 11이 점진적 정제 방식의 압축 하이트맵 스트리밍을 도입했습니다. 먼저 17 샘플 해상도로 지형을 로드하고, 그다음 33, 그다음 전체 65 샘플 그리드 순서로요.

순서가 예상보다 더 중요했습니다. 만약 압축 높이 청크부터 곧장 시작했다면, 모든 끊김이 모호했을 겁니다. 디코드 문제였을까, 텍스처 업로드 지연이었을까, 아니면 지오메트리 업데이트 문제였을까? 단순한 스트리밍을 먼저 시험한 덕분에 불확실성의 한 범주 전체를 제거할 수 있었습니다.

실용적인 교훈 하나가 나왔습니다. 업로드 지연은 평균 FPS를 통해서가 아니라 직접 측정하라는 것입니다. 평균은 프레임 스파이크를 숨기고, 플레이어가 실제로 느끼는 건 바로 그 프레임 스파이크입니다.

비주얼 예산 전쟁

세 개의 별도 실험이 렌더링 비용을 한데 묶지 않고 따로따로 공략했습니다. 식생 밀도와 바람 애니메이션. 절벽 면을 위한 삼면 매핑(triplanar mapping)이 적용된 멀티 레이어 지형 머티리얼. 현실적인 지형 부하 아래의 캐스케이드 섀도 맵.

식생 spike는 인스턴스를 더 적은 메시로 배칭하는 것이 잎 하나당 폴리곤 수를 줄이는 것보다 더 중요하다는 점을 드러냈습니다. 머티리얼 spike는 수직면에 대한 삼면 투영이 GPU 비용을 들일 가치가 있지만, 다섯 번째 텍스처 스플랫 레이어를 추가하는 건 그렇지 않다는 걸 알아냈습니다. 섀도 spike는 1024 해상도의 캐스케이드 3개가 2밀리초의 GPU 시간을 넘기지 않으면서 받아들일 만한 접촉 그림자를 제공한다고 판단했습니다.

팀은 단도직입적인 규칙을 채택했습니다. 어떤 기능은 측정된 프레임 타임 데이터로 자기 비용을 설명할 수 있을 때만 앞으로 나아간다는 것이죠. 초기에 정해 둔 그 제약 덕분에, 볼류메트릭 지형과 클립맵을 둘러싼 이후의 아키텍처 결정이 훨씬 깔끔해졌습니다.

프로젝트의 궤도를 바꾼 전환점

Spike 10 이전까지 우리의 머릿속 모델은 "더 큰 월드는 더 많은 지오메트리를 뜻한다"였습니다. Spike 10 이후에는 "일정한 지오메트리 예산, 카메라 중심의 링 업데이트"가 됐습니다.

지오메트리 클립맵, 즉 카메라를 중심으로 한 동심원 형태의 지형 링들이고 각 링은 점점 거칠어지는데, 이는 그리기 거리와 상관없이 삼각형 수가 대체로 일정하게 유지된다는 뜻이었습니다. 실용적인 요령은 링 경계에서의 지오모핑이었습니다. 셰이더에서 정점 높이를 부드럽게 섞어, 해상도 레벨 간의 전환이 움직이는 중에는 보이지 않게 하는 거죠.

미묘한 교훈은 테스트 방법에서 나왔습니다. 클립맵은 스크린샷에서는 멀쩡해 보입니다. 그 아티팩트는 링 경계를 가로지르는 지속적인 카메라 이동 아래에서만 드러납니다. 팀은 일정한 속도로 이동하며 시간적 노이즈를 살피는 데 시간을 들였습니다. "스크린샷은 거짓말을 했다"고 Oleg는 씁니다. "진실을 말한 건 움직임이었다."

지하로 내려가기

하이트맵은 동굴을 표현할 수 없습니다. 그리드의 한 점마다 하나의 고도 값만 저장하니까요. 터널, 돌출부, 깎인 암벽이 필요한 순간, 볼류메트릭 지형이 필요해집니다.

Spike 12는 WebGPU 컴퓨트 셰이더를 사용해 GPU에서 marching cubes를 구현했고, 3D 부호 거리장(signed distance field)에서 삼각형 메시를 추출했습니다. 64 큐브 청크 네 개가 애니메이션되는 SDF 편집으로부터 프레임마다 메시를 갱신하며 동시에 돌아갔습니다. 컴퓨트 셰이더가 모든 것을 처리했습니다. 필드 평가, 셀 분류, 정점 방출까지, CPU 리드백 없이요.

도전은 그걸 동작시키는 게 아니었습니다. 다른 모든 것과 함께 동작시키는 것이었습니다. Three.js 씬 그래프와의 통합, 버퍼 수명 관리(WebGPU 버퍼는 크기를 변경할 수 없습니다), 아직 처리 중인 GPU 리소스를 파괴하지 않기 위한 펜스 처리. 시리즈는 우리가 "점진적 강화(incremental hardening)"라고 부르는 것에 두 개의 전체 부를 할애합니다. 한 번에 하나의 기능을 추가하고, 추가할 때마다 이전 레이어가 여전히 동작하는지 확인하는, 화려하지 않은 과정 말이죠.

이음매 악몽

시리즈에서 기술적으로 가장 험난한 부분은 9부에서 11부에 걸쳐 있고, 서로 다른 해상도의 지형 청크가 만날 때 무슨 일이 벌어지는지를 다룹니다.

고해상도 청크가 저해상도 청크 옆에 놓이면, 독립적으로 생성된 두 메시는 경계에서 맞아떨어지지 않습니다. 그 결과 눈에 보이는 균열, 깜빡이는 가장자리, 그리고 빛이 새어 드는 T자 접합이 생깁니다. Transvoxel 알고리즘은 해상도 차이를 잇는 특수한 전환 셀로 이를 해결합니다. 하지만 모든 청크 구성에 걸쳐, 일관된 와인딩 순서, 적절한 버퍼 관리, 정확한 그리기 범위로 그것을 올바르게 구현하는 일은 여섯 개의 별도 실험을 잡아먹었습니다.

팀에서 가장 기억에 남는 디버깅 이야기는 이렇습니다. 이틀 동안 우리가 전환 로직 탓이라고 믿었던 이음매 아티팩트를 쫓았습니다. 진짜 범인은 오래된 데이터였습니다. GPU 컴퓨트 셰이더가 버퍼에 N개의 정점을 썼는데, 그리기 호출은 여전히 이전 프레임의 N+M개 정점을 렌더링하도록 설정돼 있었습니다. 그 여분의 정점들이 쓰레기 값을 담고 있어서 면도날처럼 얇은 삼각형들이 깜빡였던 거죠. 한 줄 수정으로 해결됐습니다. 그리기 범위를 원자 카운터의 활성 정점 수로 잘라낸 것입니다.

"렌더링 버그는 흔히 메싱 버그인 척한다"고 Oleg는 짚습니다. "지오메트리는 처음부터 내내 맞았다."

혼돈에서 거버넌스로

이음매 전투를 치른 뒤, 팀은 임시방편의 청크 동작을 명시적인 정책 시스템으로 대체했습니다. 이제 중앙 함수가 각 청크의 LOD 레벨, 렌더링 모드(하이트맵인지 marching cubes인지), 그리고 어느 면에 전환 셀이 필요한지를 결정합니다. 거리 링이 기본 LOD를 정했습니다. 인접성 제약이 이웃한 두 청크가 해상도 레벨에서 하나 이상 차이 나지 않도록 보장했습니다. 편집 비트맵은 크리에이터 수정이 담긴 볼류메트릭 청크를 거리와 상관없이 marching-cubes 모드로 유지했습니다.

색으로 구분한 디버그 오버레이, 즉 하이트맵 청크는 초록, marching cubes는 파랑, 전환 면은 주황으로 표시한 것은 "저 능선 근처 어딘가에서 버그를 봤다"를 "버그가 북서쪽을 향한 (142, 12, -67) 위치에서 나타난다"로 바꿔 놓았습니다.

"정책이 복잡도를 줄이지는 않았다"고 Oleg는 씁니다. "복잡도를 정리했다."

이 모든 것이 만들어낸 결과

마지막 spike는 클립맵 링, 프래그먼트별 하늘 안개(각 지형 프래그먼트 방향의 실제 스카이박스 색을 샘플링), 그리고 Three.js 모듈 배선을 하나의 통합된 데모로 결합했습니다. 그 결과는, 근거리 볼류메트릭 편집, 중거리 하이트맵 청크, 원거리 클립맵 링을 모드와 LOD와 전환을 다스리는 정책 레이어 아래에 겹쳐 놓은 지형 시스템입니다.

시리즈는 Oleg가 앞으로 어떤 프로젝트에서든 반복하겠다고 말하는 교훈들로 마무리됩니다.

  • 기능 작업에 앞서 위험 spike부터 시작하라. 콘텐츠 파이프라인에 투자하기 전에 "우리가 이걸 할 수나 있나?"라는 질문부터 처리하라.
  • 통합 점프 전에 검증된 베이스라인을 동결하라. 깨끗한 체크포인트를 세우는 데 들인 하루는, 나중에 회귀를 이분 탐색하는 여러 날을 아껴 준다.
  • 최적화 마라톤 전에 정책과 관측 가능성을 강제하라. 트리거 규칙이 있는 이름 붙은 조건은 매번 정체불명의 버그를 이긴다.
  • 스크린샷이 아니라 움직임 속에서 테스트하라. 팝, 깜빡임, 스트리밍 끊김은 모두 정지된 프레임 안에 숨는다.
  • 평균 FPS가 아니라 기능별 프레임 타임을 측정하라. 평균은 사용자가 실제로 느끼는 스파이크를 숨긴다.
  • 지저분한 부분을 공개하라. 잘못 든 길, 헛다리, 엉뚱한 시스템을 탓한 이틀. 사람들이 실제로 배울 수 있는 건 바로 그런 부분이다.

이것이 Cinevva를 넘어 왜 중요한가

이 시리즈는 한 회사의 지형 파이프라인을 넘어서는 세 가지 이유로 의미가 있습니다.

첫째, WebGPU 컴퓨트 셰이더, WebAssembly 물리, 그리고 엣지에 배포된 Durable Objects가 어떤 문턱을 넘었음을 보여 줍니다. 볼류메트릭 지형, 실시간 편집, 스트리밍 LOD를 갖춘 멀티플레이어 오픈 월드가 2026년에 브라우저 탭 안에서 아키텍처적으로 실현 가능합니다. 2년 전에는 사실이 아니었습니다.

둘째, spike 방법론, 즉 각각이 위험한 질문 하나를 살아 있고 측정 가능한 결과로 답하는 작고 집중된 실험들은, 잘 안 될 수도 있는 무언가를 시도하는 어느 팀에게나 하나의 템플릿을 제공합니다. 투자하기 전에 측정하고, 통합하기 전에 베이스라인을 세우고, 최적화하기 전에 엣지 케이스에 이름을 붙이는 규율은 지형 시스템을 훌쩍 넘어서 적용됩니다.

셋째, 그 과감한 투명성이 바로 핵심입니다. 막다른 길과 이틀짜리 디버깅 우회로까지 포함해 24개 실험 전부의 소스 코드를 공개한 것은, 이것을 단순한 기술 블로그 이상으로 만듭니다. 독자를 고객이 아니라 동료로 대하는 공개 엔지니어링 노트인 셈이죠.

전체 시리즈는 우리 시리즈 가이드에서 볼 수 있고, 모든 spike가 브라우저에서 실시간으로 돌아갑니다.


이 글은 원래 Medium에 게시되었습니다.