사전 glTF
포맷

glTF

gabury1

glTF 는 3차원 장면을 프로그램에서 프로그램으로 실어 나르는 파일 형식입니다. 물체의 모양과 색과 움직임을 한 벌로 적어 두고, 받는 쪽이 읽자마자 화면에 그릴 수 있게 합니다. 모델을 만들면서 쓰던 편집용 데이터는 담지 않습니다.

쉽고 빠른 이해

glTF 는 3차원 모델을 옮겨 싣는 파일 형식입니다. 쇼핑몰에서 상품을 손가락으로 돌려 보는 뷰어에 올라가는 파일이 대개 이것입니다.

모델링 도구가 저장하는 파일에는 나중에 다시 고치는 데 필요한 데이터가 잔뜩 들어 있습니다. 화면에 띄우기만 할 쪽에는 쓸모가 없는데 용량과 해석 비용은 냅니다. glTF 는 띄우는 데 필요한 것만 남겨서 받는 쪽이 변환 없이 곧장 그리게 합니다.

  1. 무엇이 어디에 놓이고 어떤 재질인지는 글로 된 설명 한 덩이에 적습니다
  2. 좌표와 움직임 같은 숫자 뭉치는 이진 덩이에 따로 담습니다
  3. 받는 쪽은 설명을 읽어 구조를 세우고, 숫자 뭉치는 잘라 고치지 않고 그래픽 카드로 올립니다

대가가 있습니다. 숫자 뭉치가 이진이라 사람이 열어 봐도 읽히지 않고, 무엇이 바뀌었는지 줄 단위로 견줄 수 없습니다. 편집용 데이터를 버린 파일이라 이것만 받아서는 원래 작업으로 되돌아가지도 못합니다.

상세

glTF 는 GL Transmission Format 의 줄임말입니다. 그래픽 라이브러리 쪽으로 실어 보내는 형식이라는 뜻입니다. 이름을 세우고 규격을 관리하는 곳은 그래픽스 표준 단체인 Khronos Group 입니다.

3차원 모델을 만드는 곳과 띄우는 곳은 대개 다른 프로그램입니다. 모델러는 저작 도구에서 만들고, 그것을 보는 사람은 브라우저나 게임 엔진에서 봅니다. 그 사이를 건너는 파일이 필요합니다.

원고를 고칠 때 쓰는 편집 파일과 남에게 보내는 인쇄용 파일이 다른 것과 같습니다. 편집 파일에는 고치는 데 필요한 것이 들어 있고, 보내는 파일에는 읽는 데 필요한 것만 들어 있습니다.

glTF 는 그중 보내는 쪽입니다. 담는 것은 화면에 그리는 일, 곧 렌더링에 필요한 네 가지입니다.

  1. 장면에 무엇이 어디에 놓여 있는지
  2. 물체의 겉면이 어떻게 생겼는지. 겉면을 삼각형 여럿으로 쪼갠 것이 메시입니다
  3. 그 표면을 무슨 색과 질감으로 칠하는지
  4. 시간이 흐르면 무엇이 어떻게 움직이는지

그래서 이 형식을 전송 포맷이라고 부릅니다. 저작 도구가 쓰는 포맷을 대신하는 것이 아니라, 작업이 끝난 결과를 내보낼 때 거치는 종착지입니다.

세 덩이로 갈리는 파일 한 벌

glTF 한 벌은 성격이 다른 세 덩이로 갈립니다. 장면 설명은 JSON(JavaScript Object Notation, 자바스크립트 객체 표기) 한 덩이로 적습니다. 좌표와 애니메이션 값 같은 숫자 뭉치는 이진 덩이에 담습니다. 표면에 입힐 그림이 텍스처입니다. 이미지 파일로 따로 둡니다.

숫자를 JSON 에 적지 않고 따로 뺀 까닭은 크기와 해석 비용입니다. 좌표 하나를 글자로 적으면 -0.692985 처럼 아홉 글자가 들고, 읽는 쪽은 그 글자를 다시 수로 바꿔야 합니다. 이진으로 적으면 같은 값이 네 바이트고 바꿀 일도 없습니다.

세 덩이를 묶는 방법은 둘입니다. .gltf 는 JSON 이 본체이고 나머지는 옆 파일로 두거나 data: 로 시작하는 주소로 JSON 안에 박습니다. .glb 는 셋을 한 파일에 담습니다. 같은 세 덩이가 어느 파일 안에 들어가는지만 갈립니다.

flowchart TD
    G["장면 설명 · JSON"]
    B["숫자 뭉치 · 이진"]
    I["텍스처 · 이미지"]
    G --> P1[".gltf — JSON 이 본체 · 나머지는 옆 파일이나 주소로"]
    B --> P1
    I --> P1
    G --> P2[".glb — 셋을 한 파일에"]
    B --> P2
    I --> P2

배열 번호로 거는 참조

JSON 본체는 최상위에 배열을 여럿 둡니다. 메시는 meshes 배열에, 재질은 materials 배열에, 숫자 덩이는 buffers 배열에 나란히 늘어놓습니다.

이 배열의 요소들은 서로를 인덱스 번호로 가리킵니다. 이름으로 가리키지 않습니다. name 칸이 따로 있기는 하지만 그것은 사람이 보라고 두는 표시이고, 겹쳐도 막지 않아서 참조에 쓸 수 없습니다.

JSON
"bufferViews": [
  { "buffer": 0,      // 0번 버퍼를 본다
    "byteOffset": 0,
    "byteLength": 288 }
]

번호로 걸면 읽는 쪽이 이름 표를 따로 만들지 않아도 됩니다. 배열을 한 번 훑어 객체를 차례로 세워 두고, 번호를 그 배열의 첨자로 쓰면 참조가 이어집니다.

좌표 숫자가 그래픽 카드까지 가는 길

숫자 뭉치는 세 겹을 거쳐 쓰입니다. 겹마다 답하는 질문이 다릅니다.

첫 겹은 버퍼입니다. 이진 덩이 한 개를 통째로 가리킵니다. 안에 무엇이 들었는지는 말하지 않고 길이만 답니다.

둘째 겹은 버퍼 뷰입니다. 그 덩이 안의 한 토막을 시작 바이트와 길이로 잘라냅니다. 좌표 토막과 삼각형 순서 토막이 한 덩이 안에 나란히 들어 있을 때 둘을 가르는 겹입니다.

셋째 겹이 액세서입니다. 토막의 바이트를 어떤 값으로 몇 개 읽을지 적습니다. 아래 액세서는 네 바이트짜리 실수를 셋씩 묶어 한 점으로 보고, 그런 점을 2,399개 읽습니다.

자료형을 적는 5126 은 배열 번호가 아닙니다. 그래픽스 인터페이스가 자료형에 매겨 두고 쓰던 번호를 그대로 빌려 온 값입니다.

JSON
"accessors": [
  { "bufferView": 1,
    "componentType": 5126,  // 실수 · 4바이트
    "type": "VEC3",         // 셋씩 한 점
    "count": 2399 }         // 점 2,399개
]

메시의 한 조각이 프리미티브입니다. 메시를 삼각형 한 묶음 단위로 쪼갠 조각입니다.

프리미티브는 정점마다 무엇을 들고 있는지를 정점 속성 이름으로 적습니다. POSITION 은 점의 좌표입니다. NORMAL 은 그 점에서 면이 어느 쪽을 향하는지를 적은 값, 곧 법선입니다. 속성 이름마다 액세서 번호를 하나씩 답니다. 삼각형을 어느 정점 순서로 잇는지도 같은 방식으로 액세서 하나를 가리킵니다.

세 겹과 프리미티브가 어떻게 이어지는지를 한 줄로 보면 이렇습니다.

flowchart TD
    A["버퍼 · 이진 덩이 하나"] --> B["버퍼 뷰 · 오프셋 + 길이로 자른 토막"]
    B --> C["액세서 · 자료형 + 개수로 읽는 법"]
    C --> D["프리미티브 · 정점 속성마다 액세서 번호 하나"]

세 겹으로 나눈 덕에 토막을 손대지 않고 쓸 수 있습니다. 받는 쪽은 바이트를 해석하지 않고 GPU(Graphics Processing Unit, 그래픽 처리 장치) 버퍼로 그대로 올린 뒤, 액세서가 적어 둔 자료형만 넘겨 줍니다.

노드 나무로 세운 장면

장면은 노드를 나무처럼 매단 구조입니다. 노드 하나가 자기 위치와 회전과 크기를 들고, 그 아래에 자식 노드를 겁니다. 부모에 걸린 변환은 자식에게 곱해져 내려갑니다.

flowchart TD
    S["장면"] --> C["자동차"]
    C --> B["차체 · 메시"]
    C --> W1["앞바퀴 · 메시"]
    C --> W2["뒷바퀴 · 메시"]

자동차 노드를 오른쪽으로 옮기면 차체와 바퀴가 함께 갑니다. 바퀴 노드만 돌리면 바퀴만 돕니다. 노드는 메시 말고 카메라나 뼈대도 가리킬 수 있습니다.

glTF 는 좌표계와 단위도 하나로 못 박습니다. 오른손 좌표계를 쓰고, 위쪽이 +Y 이고, 거리는 미터이고, 각도는 라디안입니다. 이것을 안 정해 두면 같은 파일이 프로그램마다 누워 보이거나 백 배 크게 열립니다.

금속-거칠기 재질 모델

머티리얼은 표면이 빛을 어떻게 받는지를 적은 값 묶음입니다. glTF 는 물리 기반 렌더링에서 널리 쓰이는 금속-거칠기 모델 한 가지로만 적습니다. 바탕색과, 쇠붙이에 가까운 정도와, 표면이 거친 정도 셋이 중심입니다.

거친 정도는 0 과 1 사이 값입니다. 0 이면 거울처럼 매끈하고 1 이면 빛이 사방으로 흩어집니다. 쇠붙이 정도도 0 과 1 사이에서 정합니다.

값 하나는 표면 전체에 똑같이 걸립니다. 잔주름이나 구석의 그늘처럼 점마다 달라야 하는 것은 그렇게 못 적습니다. 그런 대목에는 값 대신 그림을 걸어 점마다 다른 값을 줍니다.

그림 무엇을 담나 왜 그림으로 주나
노멀 맵 점마다 법선이 어느 쪽을 향하는지 잔주름을 삼각형으로 깎지 않고 흉내 낸다
오클루전 맵 구석이 얼마나 어두워지는지 어두워지는 정도가 곳곳마다 다르다
발광 맵 어느 부분이 스스로 빛나는지 표면 일부만 빛난다

이 그림들은 값 셋과 같은 재질 칸에 나란히 붙습니다. 한 재질이 값과 그림을 함께 드는 셈입니다.

재질 모델을 하나로 묶어 둔 까닭은 같은 파일이 어디서나 비슷하게 보이게 하려는 것입니다. 파일은 값만 적고 계산법은 적지 않습니다. 빛을 어떻게 계산할지 적은 프로그램이 셰이더입니다. 받는 쪽은 남이 쓴 셰이더를 해석할 일이 없습니다.

한 파일로 묶은 .glb

파일을 셋으로 나눠 두면 받는 쪽이 요청을 여러 번 보냅니다. 그렇다고 이진 덩이를 base64 로 바꿔 JSON 안에 박으면 용량이 3분의 1쯤 늘고 디코딩 비용이 붙습니다. 이 둘을 피하려고 한 파일로 묶은 것이 .glb 입니다.

파일 앞머리 12바이트가 무엇인지 밝힙니다.

바이트 자리 길이 담는 값
0 4 glTF 네 글자 — 이 파일이 무엇인지 알리는 표식
4 4 컨테이너 판 번호
8 4 머리까지 포함한 전체 길이

머리 뒤로는 청크가 이어집니다. 첫 청크가 JSON 이고, 이진 덩이가 있으면 둘째 청크로 붙습니다. 청크마다 길이와 종류를 앞에 달고, 시작과 끝을 4바이트 경계에 맞춥니다.

block-beta
columns 3
  h["머리 12바이트"]:3
  l1["길이"] t1["종류 · JSON"] d1["장면 설명"]
  l2["길이"] t2["종류 · BIN"] d2["이진 덩이"]

종류 칸을 앞에 둔 덕에 모르는 청크를 만나도 길이만큼 건너뛰면 됩니다. 뒤에 새 종류가 붙어도 옛 프로그램이 파일 전체를 놓치지 않습니다.

확장으로 늘리는 기능

지금 쓰는 판은 2.0 입니다. 작은 판올림은 앞뒤로 호환되므로, 2.0 만 아는 프로그램도 나중 판으로 적은 파일을 읽고 모르는 기능만 건너뜁니다.

규격에 없는 값이 필요하면 아무 객체에나 extensions 칸을 달아 넣습니다. 그리고 쓴 확장 이름을 최상위 extensionsUsed 배열에 모아 적습니다.

그중 없으면 파일을 제대로 그릴 수 없는 것은 extensionsRequired 에도 적습니다. 이 칸이 읽는 쪽의 판단 기준입니다. 여기 모르는 이름이 있으면 그 파일은 못 읽는다고 보고 멈춥니다. extensionsUsed 에만 있으면 그 확장을 무시하고 나머지를 그립니다.

flowchart TD
    A["모르는 확장 이름을 만난다"] --> B{"extensionsRequired 에 있나"}
    B -->|"있다"| C["못 읽는 파일로 보고 멈춘다"]
    B -->|"extensionsUsed 에만 있다"| D["그 확장만 건너뛰고 나머지를 그린다"]

확장 이름은 접두사로 누가 낸 것인지 밝힙니다. KHR_ 은 표준 단체가 승인한 것이고, 그 밖의 접두사는 회사나 개인이 낸 것입니다.

담지 않는 데이터

전송 포맷이라 편집으로 되돌아갈 데이터는 남기지 않습니다. 무엇이 어디로 빠졌는지를 알아야 이 파일을 어디까지 믿을지 정할 수 있습니다.

안 담는 것 어디가 받나
모델링 이력 · 변형 기능 · 곡면 원본 저작 도구의 자기 포맷
물리 · 충돌 · 게임 규칙 · 스크립트 엔진 쪽 데이터
표면 계산을 적은 셰이더 코드 그리는 쪽 프로그램

표면에 입히는 텍스처도 아무 형식이나 되지는 않습니다. 확장 없이 쓸 수 있는 것은 .png 와 .jpg 둘뿐입니다. 그래픽 카드가 압축한 채로 먹는 형식을 쓰려면 확장을 걸어야 합니다.

버전 관리에서도 값을 잃습니다. 좌표와 움직임이 이진이라 무엇이 바뀌었는지 줄 단위로 안 보이고, 두 사람이 고친 것을 합칠 수도 없습니다. 리뷰에서 볼 수 있는 것은 파일이 바뀌었다는 사실뿐입니다.

관련 항목

glTF 파일 안에 함께 들어 있는 구성 요소

씬 그래프 · 노드 · 메시 · 프리미티브 · 액세서 · 버퍼 뷰 · 버퍼 · 머티리얼 · 텍스처 · 샘플러 · 스킨 · 모프 타깃 · 카메라

glTF 가 이진 덩이에 담는 기하 데이터

정점 · 정점 속성 · 정점 버퍼 · 인덱스 버퍼 · 법선 · 텍스처 좌표 · 정점 색 · 탄젠트 · 바운딩 박스

glTF 를 정의하고 관리하는 단체와 문서

Khronos Group · JSON 스키마 · 미디어 타입 · glTF 확장 · KHR_draco_mesh_compression

glTF 와 같은 역할을 두고 겨루는 3차원 교환 포맷

USD · FBX · OBJ · COLLADA · STL · PLY · 3MF · 저작 포맷

glTF 파일을 만들어 내보내는 저작 도구

Blender · Maya · 3ds Max · Substance 3D · Sketchfab

glTF 를 읽어 화면에 띄우는 런타임과 라이브러리

Three.js · Babylon.js · Unity · Unreal Engine · model-viewer · Cesium · 3D 라이브러리

glTF 가 올라타는 그래픽스 인터페이스

WebGL · WebGPU · OpenGL · OpenGL ES · Vulkan · GPU

glTF 가 쓰는 재질과 렌더링 모델

물리 기반 렌더링 · 금속-거칠기 모델 · 알베도 · 노멀 매핑 · 알파 블렌딩 · 셰이더 · 렌더링

glTF 파일 크기를 줄이는 압축 방식

Draco · Meshopt · KTX2 · Basis Universal · 텍스처 압축 · gzip

glTF 가 이진 데이터를 싣는 방법

JSON · base64 · 데이터 URI · 리틀 엔디언 · 바이트 정렬 · 청크 · 매직 넘버

다른 이름: GL Transmission Format · gltf