3D 프롭 피벗 방향과 바닥 접지 오차를 자동 정렬하는 에셋 전처리 툴
3D 프롭의 피벗 방향과 바닥 접지 오차를 자동 분석하고 보정하는 전처리 파이프라인을 소개합니다. 좌표계 정렬부터 접촉점 계산, 검증 지표까지 배치 품질을 높이는 구현 방법을 정리했습니다.

핵심 요약
3D 프롭을 씬에 놓았을 때 어딘가 기울어 보이거나 바닥에서 뜨는 문제는 대개 피벗 방향과 접촉점에서 시작합니다. 전처리 툴이 로컬 좌표계의 Up·Forward 축과 바닥에 닿는 정점의 위치를 미리 정리해 두면 배치할 때마다 수동으로 돌리고 옮기는 일을 크게 줄일 수 있습니다.
처리는 크게 세 단계로 나뉩니다.
- 프롭의 Up·Forward 방향을 표준 좌표계로 정렬합니다.
- 바닥 접촉 정점의 낮은 높이 백분위수와 중심점을 계산합니다.
- 보정된 루트 피벗과 메시 오프셋을 저장하고 자동 검증합니다.
왜 3D 프롭은 배치할 때 기울거나 바닥에서 떠 보이는가?
겉으로는 배치 실수처럼 보이지만 실제 원인은 제작 단계의 좌표계와 피벗 설정인 경우가 많습니다.
- 모델링 툴과 게임 엔진의 Up 축이 다릅니다.
- FBX 또는 glTF 변환 과정에서 Forward 축이 바뀝니다.
- 피벗이 오브젝트 중심이나 임의의 원점에 남아 있습니다.
- 바닥에 닿아야 할 정점보다 장식용 돌출부가 더 낮게 배치되어 있습니다.
- 스케일이 적용되지 않아 접지 오차의 실제 크기를 판단하기 어렵습니다.
| 증상 | 주요 원인 | 전처리 단계의 대응 |
|---|---|---|
| 프롭이 90도 회전함 | Up·Forward 축 불일치 | 좌표계와 축 메타데이터 정규화 |
| 바닥에서 떠 있음 | 피벗의 Y 위치가 높음 | 접촉 정점의 최저 높이 보정 |
| 바닥을 파고듦 | 돌출부 또는 음수 오차 | 로버스트 최저 높이와 여유값 사용 |
| 복제 배치마다 방향이 다름 | 로컬 Forward 축 불명확 | 방향 후보 평가와 신뢰도 표시 |
| 스냅 위치가 일정하지 않음 | 중심 피벗 사용 | 바닥 접촉 영역의 중심을 피벗으로 설정 |
이런 문제를 에셋마다 손으로 고치면 프롭 수가 늘어날수록 결과가 달라집니다. 아티스트가 반복하는 좌표계 정렬과 접지 보정을 전처리 규칙으로 옮겨 두는 편이 훨씬 관리하기 쉽습니다.

자동 정렬 툴이 받아야 할 것과 내보낼 것
전처리 툴은 메시를 한 번 회전시키고 끝나는 도구가 아닙니다. 원본 정보와 보정 결과를 따로 저장해야 나중에 규칙을 다시 적용하거나 보정을 되돌릴 수 있습니다.
| 구분 | 입력 예시 | 출력 예시 |
|---|---|---|
| 메시 데이터 | 정점, 인덱스, 바운딩 박스 | 정렬된 회전값과 오프셋 |
| 좌표계 정보 | Up 축, Forward 축, 단위 | 표준 좌표계 기준의 Basis |
| 접지 설정 | 바닥 높이, Clearance | 루트 피벗 위치 |
| 예외 처리 | 수동 축 지정, 잠금 옵션 | 자동 처리 여부와 경고 |
| 검증 기준 | 허용 기울기와 접지 오차 | PASS, WARN, FAIL 상태 |
출력 구조는 단순하게 잡는 편이 좋습니다.
AssetRoot
└── MeshInstance
AssetRoot는 게임 플레이와 배치 도구가 참조하는 표준 피벗입니다. 실제 메시와 렌더러는 MeshInstance 아래에 두고 전처리 결과에 따라 이 자식의 로컬 위치와 회전을 보정합니다. 이렇게 하면 원본 메시를 직접 수정하지 않아도 됩니다.
3D 프롭의 피벗 방향을 자동으로 추론하는 방법
1. 명시적 메타데이터를 가장 먼저 사용한다
자동 추론보다 확실한 방법은 에셋 옆에 좌표계 메타데이터를 함께 두는 것입니다. 예를 들면 다음과 같습니다.
{
"up": "Y",
"forward": "Z",
"pivot": "ground-center",
"clearance": 0.0,
"autoRotate": true
}
문이나 차량처럼 Forward 방향 자체가 디자인 의도와 연결된 프롭은 메시 모양만 보고 방향을 정하기 어렵습니다. 이런 에셋은 아티스트가 Forward 축을 지정하고 툴은 접지 보정과 검증을 맡기는 편이 안전합니다.
2. 메타데이터가 없을 때 축 후보를 평가한다
축 정보가 없다면 메시 정점에 주성분 분석(PCA)을 적용해 주요 방향 후보를 만들 수 있습니다. 다만 PCA만으로 Up과 Forward를 항상 맞힐 수 있는 것은 아닙니다. 원기둥이나 둥근 바위, 대칭적인 장식물처럼 어느 방향에서 봐도 비슷한 모양인 에셋이 있기 때문입니다.
실무에서는 아래 항목을 점수로 만들어 후보를 비교합니다.
- 해당 축을 Up으로 사용했을 때의 높이 비율
- 바닥 근처 정점이 만드는 접촉 영역의 크기
- 무게 중심이 접촉 영역 안에 들어오는 정도
- 바닥에 놓았을 때의 기울기 오차
- 좌우 또는 전후 비대칭성
후보 축의 점수는 예를 들어 다음처럼 구성할 수 있습니다.
여기서 는 높이 비율, 는 접촉 영역 점수, 는 안정성 점수, 는 방향 비대칭성 점수입니다. , , , 는 프로젝트의 에셋 성격에 맞춰 조정합니다.
1위와 2위 후보의 점수 차이가 작다면 자동으로 확정하지 말고 LOW_CONFIDENCE 경고를 남깁니다. 자동화의 목적은 애매한 결과까지 억지로 확정하는 데 있지 않습니다. 사람이 확인할 에셋을 빠르게 골라내는 것만으로도 충분히 가치가 있습니다.
3. 표준 Basis를 구성한다
선택한 Up 벡터를 , Forward 벡터를 라고 하겠습니다. 두 벡터를 정규화하고 직교화하면 Right 벡터를 만들 수 있습니다.
이후 Forward를 한 번 더 계산합니다.
이렇게 만든 , , 를 원본 로컬 좌표계의 Basis로 사용하고 목표 좌표계의 Basis와 비교해 회전 변환을 구합니다. 여기서 cross의 순서를 바꾸면 좌표계가 뒤집힐 수 있습니다. 엔진의 오른손·왼손 좌표계와 축 규칙은 초기에 정해 두고 끝까지 같은 규칙을 써야 합니다.
바닥 접지 오차를 계산하고 보정하는 방법
회전을 끝내면 모든 정점을 정렬된 로컬 좌표계로 변환합니다. 각 정점의 높이를 확인해 바닥 위치를 구한 뒤, 필요한 만큼 메시를 위아래로 이동합니다.
정점 위치를 , 정렬 회전을 이라고 하면 정렬된 높이는 다음과 같습니다.
가장 단순한 방법은 최저 높이를 그대로 사용하는 것입니다.
하지만 장식용 돌출부 하나나 잘못된 정점 하나 때문에 결과가 크게 틀어질 수 있습니다. 실무 툴에서는 낮은 높이 백분위수와 접촉 밴드를 함께 사용해 이런 이상치를 완화합니다.
heightPercentile = percentile(allHeights, 1.0)
contactEpsilon = max(assetHeight * 0.01, sceneUnit * 0.001)
contactVertices = vertices where height <= heightPercentile + contactEpsilon
접촉 정점의 X·Z 평균을 바닥 피벗의 중심으로 사용합니다.
피벗의 위치를 , , 로 두고 Clearance를 라고 하면 메시를 수직으로 옮길 양은 다음과 같이 계산합니다.
Clearance는 프롭의 종류에 따라 달라집니다. 일반적인 바닥 소품은 0에 가깝게 두고 바닥에 완전히 닿지 않아야 하는 식물이나 천 장식물에는 작은 양의 값을 줄 수 있습니다.
에셋 전처리 파이프라인을 구성하는 순서
회전과 접지 보정이 서로 꼬이지 않게 하려면 처리 순서를 고정해 두는 것이 좋습니다.
- 단위와 스케일을 정규화합니다. 원본 메시의 스케일을 적용하고 프로젝트 기준 단위로 변환합니다.
- Up·Forward 축을 결정합니다. 메타데이터를 우선 사용하고 없을 때만 후보 축 평가를 실행합니다.
- 메시를 표준 좌표계로 회전합니다. 목표 Up을 엔진의 Up 축에 맞추고 Forward도 함께 정렬합니다.
- 접촉 정점을 추출합니다. 최저 높이 백분위수와 상대 오차 밴드를 사용합니다.
- 피벗 중심을 계산합니다. 접촉 정점의 중심 또는 지지 영역의 중심을 선택합니다.
- 루트와 메시 오프셋을 저장합니다. 루트는 배치 기준점으로 사용하고 메시 자식은 시각적 보정값을 가집니다.
- 검증 지표를 기록합니다. 접지 오차, 기울기, 스케일, 자동 추론 신뢰도를 저장합니다.
flowchart TD
A[원본 메시 수집] --> B[단위와 스케일 정규화]
B --> C{축 메타데이터 존재}
C -->|예| D[명시적 Up Forward 사용]
C -->|아니오| E[PCA와 형상 점수로 축 후보 평가]
D --> F[표준 좌표계 회전]
E --> F
F --> G[접촉 정점과 바닥 높이 계산]
G --> H[루트 피벗과 메시 오프셋 생성]
H --> I[오차 검증 및 결과 저장]
Python 스타일 의사 코드
다음은 특정 엔진 API에 종속되지 않은 의사 코드입니다. 핵심 처리 순서만 남겨 두었습니다.
def preprocess(vertices, up_axis, forward_axis, target_up, target_forward, clearance, scene_unit=1.0):
basis = make_orthogonal_basis(up_axis, forward_axis)
target_basis = make_orthogonal_basis(target_up, target_forward)
rotation = rotation_between(basis, target_basis)
aligned = [rotation.transform_point(v) for v in vertices]
heights = [v.y for v in aligned]
asset_height = max(heights) - min(heights)
low_height = percentile(heights, 1.0)
epsilon = max(asset_height * 0.01, scene_unit * 0.001)
contacts = [v for v in aligned if v.y <= low_height + epsilon]
contact_center = average(contacts)
ground_offset = clearance - contact_center.y
root_pivot = Vector3(contact_center.x, clearance, contact_center.z)
mesh_offset = Vector3(-contact_center.x, ground_offset - clearance, -contact_center.z)
return {
'rotation': rotation,
'rootPivot': root_pivot,
'meshOffset': mesh_offset,
'contactCount': len(contacts),
'groundError': abs(root_pivot.y + mesh_offset.y + contact_center.y - clearance)
}
실제 구현에서는 make_orthogonal_basis, rotation_between, percentile을 프로젝트의 수학 라이브러리나 엔진 API로 바꾸면 됩니다. 순서가 핵심입니다. 피벗을 먼저 옮기고 회전하는 것이 아니라 기준 좌표계로 회전한 결과에서 접촉점을 계산해야 합니다.
바닥 접촉점을 AABB 최저점 하나로 처리해도 될까?
상자처럼 단순한 프롭은 AABB 최저점만으로도 충분할 수 있습니다. 하지만 복잡한 프롭에서는 다음 문제가 생깁니다.
- 삼각형 끝부분 하나가 바닥 높이를 결정합니다.
- 계단형 메시의 일부만 바닥에 닿습니다.
- 여러 다리가 있는 프롭은 평균점이 실제 지지 영역 밖에 놓일 수 있습니다.
- 바닥이 평면이 아니라 경사면이나 불규칙한 지형입니다.
그래서 피벗 모드를 최소한 세 가지로 나누는 편이 좋습니다.
| 피벗 모드 | 계산 방식 | 적합한 에셋 |
|---|---|---|
ground-center | 접촉 정점의 평균 중심 | 상자, 가구, 일반 소품 |
support-centroid | 접촉 정점의 볼록 껍질 중심 | 다리와 받침대가 있는 프롭 |
artist-marker | 별도 마커 위치 사용 | 문, 차량, 비대칭 장식물 |
지지 영역이 중요한 프롭이라면 접촉 정점의 2D 볼록 껍질을 구하고 그 다각형의 중심을 사용하는 편이 안정적입니다. 다만 바닥에 닿는 부분이 여러 군집으로 나뉘면 군집별로 분리해 지지 영역을 평가해야 합니다.

자동 정렬 결과를 무엇으로 검증할까?
전처리 결과를 눈으로만 확인하면 작은 오차를 놓치기 쉽습니다. 결과 패널에 아래 지표를 표시해 두면 배치 품질을 수치로 관리할 수 있습니다.
| 검증 지표 | 계산 방법 | 예시 판정 기준 |
|---|---|---|
| 바닥 오차 | 보정 후 접촉 높이와 Clearance의 차이 | 자산 높이의 0.1% 이하 |
| 기울기 오차 | 정렬된 Up과 목표 Up의 각도 | 0.5도 이하 |
| 피벗 중앙성 | 피벗과 접촉 영역 중심의 거리 | 프로젝트 기준 이하 |
| 접촉 정점 수 | 접촉 밴드에 포함된 정점 수 | 최소 개수 미만이면 경고 |
| 추론 신뢰도 | 1위와 2위 후보 점수 차이 | 임계값 미만이면 수동 확인 |
수치 기준은 프로젝트의 단위와 메시 밀도, 레벨 디자인 방식에 맞춰 정해야 합니다. 크기가 다른 에셋을 같은 절대 오차로 평가하면 작은 소품에는 너무 엄격하고 큰 구조물에는 너무 느슨해집니다. 상대 오차와 절대 오차를 함께 쓰는 이유가 여기에 있습니다.
groundError <= max(absoluteTolerance, assetHeight * relativeTolerance)
검증 결과는 JSON, CSV 또는 엔진 임포트 메타데이터로 저장하면 됩니다. CI 파이프라인에서 FAIL 상태의 에셋을 막아 두면 배치 단계에서 발견되던 문제를 아트 산출물 단계에서 바로 잡을 수 있습니다.
구현에서 자주 빠뜨리는 예외
애니메이션이 있는 에셋
캐릭터나 움직이는 기계 프롭의 피벗을 정적 메시처럼 바꾸면 본 계층과 애니메이션 루트가 어긋날 수 있습니다. 애니메이션 클립이 들어 있는 파일은 메시 피벗에 값을 직접 굽기보다 별도의 배치 루트나 프리팹 루트를 추가하는 편이 안전합니다.
콜라이더와 렌더 메시의 차이
렌더 메시의 바닥과 콜라이더의 바닥은 서로 다를 수 있습니다. 배치 기준을 시각적 접지로 할지 물리적 접지로 할지 먼저 정해야 합니다. 필요하다면 렌더 메시와 콜라이더를 각각 검사합니다.
단위가 섞인 파일
어떤 파일은 센티미터, 다른 파일은 미터를 기준으로 만들어졌을 수 있습니다. 접지 오차의 임계값을 계산하기 전에 파일 단위와 엔진 단위를 확인해야 합니다. 그렇지 않으면 0.001이라는 숫자가 에셋마다 전혀 다른 의미가 됩니다.
대칭형 또는 구형 프롭
형상만으로 Forward 방향을 정할 수 없는 에셋에는 자동 회전을 적용하지 않는 편이 낫습니다. 방향이 중요하지 않다면 Up만 정렬하고 Forward는 원본을 유지하도록 선택할 수도 있습니다.
자주 묻는 질문
피벗을 메시 데이터에 직접 적용해야 하나요?
그럴 필요는 없습니다. 원본 메시를 보존해야 하거나 애니메이션·콜라이더가 연결된 경우에는 AssetRoot를 추가하고 자식 메시의 로컬 오프셋만 보정하는 편이 안전합니다.
최저 정점 하나만 사용하면 안 되나요?
작은 돌출부나 잘못된 정점 하나 때문에 오차가 커질 수 있습니다. 낮은 높이 백분위수와 접촉 밴드를 사용하고 필요하면 지지 영역의 중심까지 계산하는 편이 안정적입니다.
Forward 축도 완전히 자동으로 찾을 수 있나요?
모든 프롭의 방향을 신뢰성 있게 자동 추론하기는 어렵습니다. 비대칭 형상은 후보 점수로 어느 정도 처리할 수 있지만 문·차량·대칭형 장식물은 메타데이터나 아티스트 마커를 우선해야 합니다.
정리
3D 프롭을 안정적으로 배치하려면 피벗 위치를 에셋마다 손으로 맞추기보다 좌표계 정렬, 접촉 정점 분석, 오프셋 보정, 결과 검증을 하나의 전처리 파이프라인으로 묶는 편이 낫습니다.
축 메타데이터가 있으면 그것을 먼저 사용하고 정보가 없을 때만 형상 기반 추론으로 넘어가는 구조가 가장 현실적입니다. 낮은 높이 백분위수와 접촉 밴드를 함께 쓰면 작은 돌출부 하나 때문에 생기는 접지 오차도 줄일 수 있습니다. 보정 결과를 루트 피벗과 메시 오프셋으로 나눠 저장하면 원본 에셋은 그대로 두면서 엔진과 레벨 배치 도구에서 일관된 스냅 동작을 유지할 수 있습니다.



