# 3ds Max → Unity FBX 익스포트 완전 가이드

3ds Max에서 리깅·스킨한 캐릭터를 **Unity로 정상 반입**하기 위한 표준 절차입니다.
"왜 이렇게 하는가"를 이해하면 씬이 달라져도 스스로 대응할 수 있습니다.

---

## 0. 성공 기준 — 딱 이 화면이 나오면 끝

Unity에 FBX를 끌어다 놓고 **Root**(최상위 오브젝트)를 선택했을 때, Inspector의 Transform이 아래와 같아야 합니다.

| 항목 | 값 |
|------|-----|
| **Position** | 0, 0, 0 |
| **Rotation** | 0, 0, 0 |
| **Scale** | 1, 1, 1 |

![목표 - Unity 씬 전체](image.png)

![목표 - Root Transform](image_1.png)

**왜 이 값이 중요한가?**
Root가 `0 / 0 / 1`로 깨끗해야 Prefab화, 애니메이션 리타게팅, 물리·콜라이더 계산이 모두 예측 가능해집니다.
Scale이 `0.01`이나 Rotation이 `-90`으로 들어가면, 그 오차가 하위 본·메시·애니메이션 전체에 누적되어 나중에 원인 추적이 매우 어려워집니다. **문제는 반입 시점에 잡는 것이 가장 쌉니다.**

---

## 1. 왜 좌표와 단위가 어긋날까 (필수 배경 지식)

3ds Max와 Unity는 **좌표계와 기본 단위가 서로 다릅니다.** FBX 파일이 그 사이를 번역하는 다리 역할을 하는데, export 옵션이 틀리면 번역이 어긋나 위 값이 깨집니다.

| 구분 | 3ds Max | Unity |
|------|---------|-------|
| **Up 축 (위쪽)** | **Z-up** (Z가 하늘) | **Y-up** (Y가 하늘) |
| **좌표계 손잡이** | Right-handed | Left-handed |
| **내부 길이 단위** | 씬 단위 자유 (**cm 권장**) | **1 unit = 1 meter 고정** |

핵심은 두 가지입니다.

- **축**: Max의 Z-up을 Unity의 Y-up으로 돌려줘야 캐릭터가 눕지 않습니다.
- **단위**: Unity는 "1 = 1미터"로 못 박혀 있습니다. Max를 cm로 작업하면 1cm = 0.01m이므로, FBX가 이 100배 차이를 올바르게 흡수해줘야 Scale이 `1`로 떨어집니다. 흡수가 안 되면 `0.01`이 그대로 남습니다.

> 이 가이드의 모든 단계는 결국 **"축을 Y-up으로, 단위를 cm 기준으로"** 맞추기 위한 것입니다.

---

## 2. STEP 1 — 씬 단위 확인 (cm 기준)

먼저 Max 씬이 어떤 단위인지 확인합니다. `Customize > Units Setup` 메뉴로 들어갑니다.

![Units Setup 메뉴 위치](image_2.png)

`Display Unit Scale`을 **Metric → Centimeters**로 둡니다.

![Units Setup - Centimeters](image_3.png)

**왜 cm인가?**
BipedPlus의 내부 계산(높이·스케일 등)이 **cm를 기준(SSOT)**으로 삼습니다. 또한 Max의 표준 export 관례가 cm이기 때문에, cm로 맞춰두면 뒤의 FBX 단위 변환이 가장 깔끔하게 맞아떨어집니다.

> 회사 파이프라인이 m(meter)로 고정돼 있어도, **최소한 리깅·export 단계에서는 cm 권장**입니다. (m 씬 지원은 툴 개선 예정 항목)

---

## 3. STEP 2 — 축 정렬 (Z-up → Y-up)

Max는 Z가 위, Unity는 Y가 위입니다. export 전에 **Root를 기준으로 방향을 Unity 관례(Y가 위, Z가 앞)에 맞춰 정렬**합니다.

![Max 리그 계층과 축](image_4.png)

> 3ds Max 기준 **Y를 위쪽, 앞(정면)을 Z축**으로 잡으면 Unity와 방향이 맞습니다.

정렬 방법: Root를 선택하고 **World 기준으로 X축 +90° 회전**합니다. (Rotate Transform Type-In → `Absolute:World` X = 90)

![World X +90도 회전](image_5.png)

**왜 X축 90°인가?**
Z-up 공간을 Y-up 공간으로 눕히려면 위쪽 벡터를 Z에서 Y로 90° 굴려야 합니다. 이 회전이 곧 두 엔진의 "위쪽" 정의 차이를 메워주는 값입니다.

---

## 4. STEP 3 — Export 대상만 선택 (스킨본 + 메시)

export할 때 **스킨 본 트리 + 메시(지오메트리)만** 선택합니다. 컨트롤러·헬퍼·리그 그룹(`Ctrl_World`, `Grp_Rig`, `Drv_Root` 등)은 **넣지 않습니다.**

![스킨본 + 메시 트리만 선택](image_6.png)

**왜 리그 전체를 넣으면 안 되는가?**
Unity가 실제로 필요로 하는 것은 **스킨드 메시 + 그것을 움직이는 스켈레톤**뿐입니다. 리그 컨트롤러/헬퍼는 게임엔진에서 쓸모없을 뿐 아니라,

- 불필요한 본 계층이 늘어 임포트가 무거워지고,
- 스케일·오프셋이 걸린 컨트롤 노드가 섞여 들어가면서 **Scale 0.01 같은 오염**을 유발합니다.

> 원칙: **"게임에 필요한 것만" 내보낸다.**

---

## 5. STEP 4 — FBX Export 옵션

### (1) Include — 애니메이션 & 디포메이션

- **Animation** 체크
- **Deformations → Skins, Morphs** 체크 (스킨 웨이트와 모프가 함께 나가야 함)

![FBX Include 옵션](image_7.png)

### (2) Advanced Options — 단위 & 축

가장 중요한 부분입니다. 앞서 설명한 단위·축 변환이 여기서 최종 결정됩니다.

| 옵션 | 값 | 의미 |
|------|-----|------|
| **Units → Automatic** | 체크 | 씬 단위를 자동 감지해 변환 |
| **Scene units converted to** | **Centimeters** | 씬이 cm이므로 cm로 내보냄 |
| **Scale Factor** | **1.0** | 추가 배율 없음 (변환은 단위가 담당) |
| **Axis Conversion → Up Axis** | **Y-up** | Unity의 위쪽 축에 맞춤 |

![FBX 단위 · 축 옵션](image_8.png)

**왜 이 조합인가?**
`Scale Factor 1.0` + `cm` + `Y-up`이면, Unity가 임포트 시 cm→m(÷100)와 축 변환을 알아서 흡수해 **Root Scale이 정확히 1.0**로 떨어집니다. 여기서 Scale Factor를 임의로 만지면 100배/0.01배 오차가 다시 생깁니다.

> Unity 쪽 up 축은 Y이므로 **반드시 Y-up**으로 내보냅니다.

---

## 6. STEP 5 — Unity 반입 검증

FBX를 Unity로 끌어와 Root를 선택하고, **[0. 성공 기준]**과 동일한지 확인합니다.

- Position 0, 0, 0
- Rotation 0, 0, 0
- Scale 1, 1, 1

여기까지 맞으면 정상 반입 완료입니다.

---

## 7. 흔한 실수 & 트러블슈팅

### 실수 1 — Root Scale이 `0.01`로 들어온다

![잘못된 예 - Scale 0.01](image_9.png)

> **기본값이 아닌 Root 본을 넣으면 안 됩니다.**

**원인**: 트랜스폼이 초기화(기본값)되지 않은 Root/컨트롤 노드를 export에 포함시킨 경우입니다. 그 노드에 걸린 `0.01` 스케일(또는 cm↔m 미변환분)이 그대로 Unity Root에 baked-in 됩니다.

**해결**:
- Export 대상에서 **컨트롤러/헬퍼 Root를 제외**하고, 트랜스폼이 깨끗한 스킨 본 트리 + 메시만 내보냅니다. (STEP 3 참고)
- FBX 옵션의 `Scale Factor`가 `1.0`, `Units = Centimeters`인지 재확인합니다.

---

### Q&A — Scale에 `0.9999999`가 들어와요. 문제인가요?

![Scale 0.9999999](image_10.png)

**결론: 정상입니다.** 3D 그래픽은 값을 **부동소수점(floating point)**으로 저장하기 때문에, 여러 번의 행렬 변환(회전·단위 환산 등)을 거치면 `1.0`이 `0.9999999`처럼 미세하게 떨어질 수 있습니다. 이는 오차라기보다 **연산의 자연스러운 반올림 결과**이며, 실사용에 영향이 없습니다.

**그래도 값을 깔끔하게 `1.0`으로 만들고 싶다면**: 해당 노드의 트랜스폼을 **PRS 채널로 리셋**한 뒤, Position은 **Position Constraint**, Rotation은 **Orientation Constraint**로 교체합니다. (Transform Script 대신 순수 PRS로 정리)

![Transform Script → PRS + Constraint](image_11.png)

이렇게 하면 누적 오차 없이 값이 정리됩니다.

![정리 후 - Scale 1.0](image_12.png)

---

## 8. 최종 체크리스트

내보내기 전에 이 5줄만 확인하면 됩니다.

- [ ] **단위**: 씬이 Centimeters (`Customize > Units Setup`)
- [ ] **축**: Root를 World X **+90°** 회전 (Y-up 정렬)
- [ ] **선택**: 스킨 본 트리 + 메시만 (컨트롤러·헬퍼 제외)
- [ ] **FBX 옵션**: Units `Automatic / cm`, Scale Factor `1.0`, Up Axis `Y-up`, Animation·Skins·Morphs 체크
- [ ] **Unity 검증**: Root Transform = `0/0/0 · 0/0/0 · 1/1/1`

> `0.9999999` 스케일은 정상 오차이며, 신경 쓰일 경우에만 PRS 리셋 + Constraint로 정리합니다.