$

케이스 13 · 3D 파이프라인

3D 에셋 — 「불가능」이라 적었던 두 가지를 테스트가 뒤집었다

호러 게임에 조각상이 필요해서, 텍스트 프롬프트에서 브라우저가 실제로 받을 수 있는 메시까지 가는 선을 만들었다. 에셋 하나에 8분, 13.13MB를 421KB로. 그러고 나서 한계 항목에 두 가지를 「불가능」이라고 적었다 — 부분 수정과 폴리곤 예산. 둘 다 틀렸다. 하나는 기록을 원래 맥락에서 떼어 옮긴 탓이고, 하나는 존재하지 않는 이름으로 파라미터를 부른 탓이다. 파이프라인보다 이 틀린 방식이 더 옮겨쓸 만해서 둘 다 남긴다.

  • 3D
  • Asset Pipeline
  • Optimization
  • Verification

$

문제의식

게임은 조각상으로 가득한 방이고 그중 일부가 살아 있다. 그리고 웹 포털로 나간다 — 즉 조각상 하나하나가 플레이 전에 네트워크로 도착해야 한다. 여덟 종류를 손으로 모델링하는 건 1인 규모에서 선택지가 아니었고, 대안인 생성은 웹에서 못 쓰는 파일을 뱉는다. 보물상자 하나가 변환 직후 13MB에 삼각형 3만 개다. 그래서 질문은 「3D를 만들 수 있는가」가 아니었다. 만들어진 것을 브라우저가 받을 크기까지 줄여도 형태가 버티는가였다.

만든 선

  1. 컨셉 — 일부러 격리해서

    텍스트에서 평평한 배경 위의 단일 오브젝트로. 형태는 두껍게, 얇은 잔가지는 금지. 미감의 문제가 아니다 — 장면형 컨셉과 가느다란 디테일은 변환에서 깨진다. 이 세트의 죽은 나무를 「발톱 같은 가지, 얇은 트위그 없이」로 지정한 이유가 그것이다.

  2. 이미지에서 메시로, PBR까지

    약 6분, 텍스처 맵 네 장이 함께 나온다 — 기본색·거칠기/금속·노멀·발광. 5천 삼각짜리 상자에서 나무결이 보이는 건 노멀맵 덕이다. 디테일이 형태가 아니라 텍스처에 있다.

  3. 줄이기 — 이 순서로

    메시 감축 → 텍스처 WebP → 미사용 제거 → 정점 병합 → 지오메트리 압축. 순서가 중요한 건 무게의 대부분이 텍스처이기 때문이다. 상자 13.13MB 중 11MB가 2048×2048 맵 네 장이었다. 메시는 상대적으로 싸다.

  4. 엔진에 넣고 눈으로

    수치는 얼굴이 무너졌는지 알려주지 않는다. 모든 에셋을 three.js에서 같은 조명으로 렌더해 원본과 대조한다 — 이 사이트에 올라간 자산들이 그 결과물이기도 하다.

상자 하나, 다섯 단계

위 에셋에서 실측. 최종 파일은 변환 직후의 1/32이다.

단계삼각형크기
변환 직후30,36713.13 MB
메시 감축14,110
텍스처 → WebP14,1102.00 MB
지오메트리 압축14,1101.60 MB
모바일 프로파일5,3060.41 MB

같은 물건, 32배 차이

같은 조명, 같은 각도. 자물쇠·리벳·나무결이 감축을 견딘다. 우물은 정면 이미지 한 장으로 만들었는데 뒷면이 존재한다 — 게임에 들어갈 물건이라면 그게 전부를 가르는 질문이다.

  • 컨셉 이미지 — 텍스트에서, 15초

    컨셉 이미지 — 텍스트에서, 15초

    평평한 배경과 두꺼운 형태는 취향이 아니라 요건이다. 부서지기 쉬운 디테일은 변환을 못 넘긴다.

  • 변환 직후 — 13.13MB · 30,367 삼각

    변환 직후 — 13.13MB · 30,367 삼각

    정확하지만 웹에서는 못 쓴다.

  • 최적화 후 — 0.41MB · 5,306 삼각

    최적화 후 — 0.41MB · 5,306 삼각

    32배 작다. 이 크기에서 차이를 찾아보라 — 그게 요점이다.

  • 뒷면 135° — 단일 이미지 입력

    뒷면 135° — 단일 이미지 입력

    나무판과 금속 밴드가 뒤까지 이어진다. 이게 없으면 모델이 아니라 간판이다.

틀린 곳 1

부분 수정이 불가능하다고 적었다. 생성 메시는 재질이 하나이고 텍스처가 통짜로 구워지므로 「자물쇠만 은색으로」는 전체 재생성이라고. 근거로 삼은 건 이 게임의 예전 빌드에서 남긴 기록이었다. 그런데 그 기록은 three.js 런타임에서 재질을 틴트하려던 이야기였고, 거기서는 재질이 하나면 모델 전체가 물드는 게 맞다. 텍스처 파일을 편집하는 건 아예 다른 작업이다. GLB를 풀면 맵이 평범한 이미지 파일로 떨어진다. 황동색 픽셀만 색상값으로 골라 바꾸고 다시 묶었다. 텍스처의 0.63%가 바뀌었고, 자물쇠는 은색이 됐고, 나무와 밴드와 리벳은 그대로였다. 기록은 참이었다. 참이던 상황에서 내가 떼어 온 것이다.

자물쇠만, 자물쇠만

왼쪽부터: GLB에서 빠져나온 텍스처, 그리고 수정 전후. 여전히 불가능한 건 형태 변경이다 — 더 큰 자물쇠, 손잡이 추가. 색과 질감은 되고, 형태는 재생성이 필요하다.

  • baseColor 텍스처 2048² — 좌상단이 자물쇠

    baseColor 텍스처 2048² — 좌상단이 자물쇠

    물건 전체가 한 장에 펼쳐져 있다. 모든 표면이 이 시트 어딘가에 있다.

  • 수정 전 — 황동 자물쇠

    수정 전 — 황동 자물쇠

    원본 텍스처 그대로.

  • 수정 후 — 0.63% 픽셀 치환

    수정 후 — 0.63% 픽셀 치환

    색상값으로 골라서 마스크도 UV 조회도 필요 없었다. 나머지는 바이트 단위로 동일하다.

틀린 곳 2

폴리곤 예산도 주문할 수 없다고 적었다. 변환기가 항상 3만 삼각 안팎을 뱉으니 5,000을 원하는 의뢰는 후처리로만 맞출 수 있다고. 근거는 「해당 파라미터를 지원하지 않는다」는 API 응답이었다. 지원되지 않은 이유는 내가 `polycount`라고 불렀기 때문이고, 그런 이름은 없다. 진짜 이름은 `target_polycount`이고 100부터 300,000까지 받는다. 6,000 쿼드를 요청하니 6,084가 나왔다 — 1.4% 오차. 그리고 `quad` 토폴로지 옵션이 있는데 이게 개수보다 중요하다. 쿼드 메시는 모델러가 열어서 이어 작업할 수 있는 형태라, 생성 에셋과 수작업 에셋 사이의 간격을 대부분 메운다. 거부된 호출 하나가 「기능이 없다」를 대신하고 있었다.

6,000 요청, 6,084 수령

같은 소스 이미지, 나머지도 동일. 정점이 3분의 1로 준다 — 렌더러가 실제로 값을 치르는 부분이 그쪽이다.

요청결과정점크기
지정 없음30,367 tris27,66713.13 MB
target_polycount: 6000 · quad12,169 삼각 = 6,084 쿼드8,6370.38 MB

방법론으로 남은 것

두 오류의 모양이 같다. 좁은 근거를 일반 명제로 올렸고, 확인할 테스트를 돌리지 않았다. 그래서 규칙 둘. 다른 프로젝트의 기록을 인용할 때는 전제 조건까지 같이 옮길 것 — 특정 조건에서의 실패는 도구의 성질이 아니다. 그리고 API가 무언가를 거부하면 기능이 없다고 결론내기 전에 이름을 의심할 것. 모델 카탈로그에 진짜 파라미터가 적혀 있고 읽는 데 몇 초 걸린다. 여기서 틀린 대가는 낭비한 시간이 아니었다. 두 한계를 서비스 설명에 적기 직전이었고, 그랬다면 실제로 가진 능력을 스스로 부정하며 팔 뻔했다.

이 선이 실제로 보장하는 것

  1. POLY BUDGET

    생성 단계에서 주문, 후처리에서 재확인

  2. QUAD TOPOLOGY

    모델링 도구에서 열어 이어 작업 가능

  3. PBR · 4 MAPS

    디테일이 형태가 아니라 텍스처에

  4. TWO PROFILES

    웹 1.4MB · 모바일 0.4MB

  5. COLOUR EDITS

    텍스처 단위, 재생성 없이

  6. SHAPE = REGEN

    남은 진짜 한계

$

근거 · 깃·위키 인용

  • 위키 · 파이프라인·실측·정정 2건 · undefined-studio-wiki/architecture/3d-asset-pipeline.md
  • 위키 · 이 세션 활동 기록 · undefined-studio@76696f4
  • Keep Watching에 출하된 조각상 · game-lab/wiki/games/keep-watching.md