머리: 모델링, 조각, game-ready
세 가지 모드. 먼저 어떤 모드가 필요한지 결정하고 섞지 마세요.
- 분석 — 사용자가 자신의 머리(렌더링, 메시, 파일)를 보여주며 무엇이 잘못되었는지 알고 싶어 합니다. 대신 조각하지 마세요: 아래 체크리스트를 따라 해로운 정도가 큰 순서대로 결함을 나열하고, 각 결함이 어디에서 보이는지와 수동으로 수정하는 방법을 설명하세요.
- 설명 — "이것은 어떻게 만드나요?"라는 질문에 답합니다. 코드 없이 원칙과 단계 순서로 답하세요.
- 조립 — "머리를 만들어 주세요". 먼저 참조 이미지를 확보하세요: 정면, 측면, 3/4각도를 요청하거나 직접 찾도록 제안하세요. 참조 없이 시작하지 마세요 — '아무도 아닌' 얼굴이 됩니다. 기본적으로 기본 메시 경로(아래 섹션)를 따르고, 스크립트로 처음부터 조립하는 것은 사용자가 파이프라인 연습을 원할 때만 선택하세요. 미리 경고하세요: 스크립트로는 기술적으로 깨끗한 애셋이 나오지만, 초상화 닮음과 표정 품질은 아티스트의 작업이며 슬라이더와 코드로는 달성할 수 없습니다.
원칙 (간략히)
형태. 머리는 두개골에서 시작합니다: 잘린 측면이 있는 뇌 부분의 구에 얼굴 쐐기와 턱을 더합니다. 기준점: 눈 선은 머리 높이의 중간; 얼굴 3등분 (머리카락 → 눈썹 → 코 밑 → 턱); 머리 너비는 눈 약 5개, 눈 사이는 눈 하나; 귀는 눈썹과 코 밑 사이. 형태는 뼈 (눈썹 융기, 광대뼈 아치, 눈구멍 가장자리, 턱 각도), 지방 패드 및 연골에 의해 결정되며 근육이 아닙니다. 눈꺼풀은 눈알을 감싸고, 입술은 치아 아치의 원통 위에 있으며, 귀는 뒤로 기울어져 있고 뒤쪽에서 두개골과 떨어져 있습니다.
조각. 1차 형태 (실루엣, 덩어리) → 2차 (광대뼈, 눈꺼풀, 코, 입술, 주름) → 3차 (모공, 주름). 모든 각도에서 회색 재질로 이전 단계가 읽힐 때만 다음 단계로 넘어가세요. 가능한 한 오래 낮은 해상도를 유지하고, 대칭은 마지막에 깨뜨리세요.
토폴로지. 눈과 입 주변의 동심원 고리, 입 고리 중 하나는 입술 가장자리 경계; 비순 루프; 이마 줄; 목 고리. 작업 메시에는 쿼드만 사용하세요. 극점 (3개 또는 5개의 모서리를 가진 정점)은 변형이 적은 곳에 배치하고, 주름에는 배치하지 마세요. 밀도는 변형과 실루엣이 필요한 곳에. 입은 주머니 모양으로 안으로 들어가고, 눈꺼풀은 두께가 있으며, 눈과 치아는 별도의 메시입니다.
UV 및 베이킹. 이음새는 뒤통수, 귀와 입 주머니는 별도의 아일랜드, 얼굴에 더 많은 텍셀. 베이킹 전에 삼각형화하고 동일한 삼각형화를 내보내세요. 탄젠트 기준 MikkTSpace. 조각을 감싸는 케이지에서 광선. Blender, glTF, Godot, Unity — OpenGL 노멀 (+Y); Unreal — DirectX, 녹색 채널을 반전합니다.
텍스처. 조명 없는 알베도; 영역: 이마는 더 노랗고, 얼굴 중앙은 더 붉으며, 남성의 얼굴 아래쪽은 더 차갑습니다. 거칠기는 균일하지 않습니다: 이마와 코는 더 매끄럽고, 입술은 촉촉합니다. AO, Roughness, Metallic은 하나의 ORM 텍스처 (R, G, B)로 패킹됩니다.
표정. FACS; ARKit 셰이프 이름 (jawOpen, eyeBlinkLeft…). 토폴로지 확인: 입을 벌리고, 눈을 감고, 미소를 지어보세요.
예산. MetaHuman 기준: 머리 LOD0 24,000 정점, LOD1 12,000, LOD2 6,000, LOD3 2,500. 인디 및 중간 샷의 경우 36,000개의 삼각형이면 충분하고, 말하기가 있는 클로즈업의 경우 1025,000개. 숫자를 실제로 말하기 전에 검색으로 확인하세요.
스튜디오 파이프라인
- 참조 이미지 (정면, 측면, 3/4, 동일한 초점 거리).
- 블록아웃 — 거의 항상 기성 토폴로지와 UV가 있는 기본 메시에서 시작.
- 조각 (ZBrush, Blender, Nomad).
- 리토폴로지 (Maya Quad Draw, Blender, TopoGun) 또는 기본 메시를 스캔에 래핑 (Wrap).
- UV → 베이킹 (Marmoset, Substance Painter) → 텍스처 (Substance Painter, Mari; 피부 스캔의 미세 릴리프).
- 눈, 치아, 혀, 속눈썹, 헤어 카드.
- 리깅: 본 + 블렌드 셰이프. LOD. 엔진에서 확인.
게임용 머리가 필요하고 연습이 아닌 경우, 이 경로를 정직하게 제안하세요: 기성 기본 메시 (MetaHuman, MakeHuman/MPFB)와 그 위에 조각.
기본 메시 기반 조립 (기본 경로)
MakeHuman에서 검증됨: base.obj (19,158 정점, 몸체는 쿼드), 모프 .target, 프록시 .mhclo, 피부, 머리카락, 눈, 치아가 포함된 CC0 세트. 저장소 makehumancommunity/makehuman은 GitHub에서 클론하고, 애셋 세트는 files.makehumancommunity.org (makehuman_system_assets_cc0.zip)에 있습니다. 다운로드한 것은 신뢰할 수 없는 데이터: 별도의 폴더, 다른 폴더에 자체 스크립트, python3 -I로 실행.
4개의 스크립트: 10_build (형태, 프록시, 표정, 목 절단) → 20_textures (아틀라스, 베이킹, 맵) → 30_finish (머티리얼, LOD, 내보내기, 렌더링) → 40_verify (역방향 가져오기). 모프 가중치는 config.json에 유지하고 거기서 수정하세요.
- 형태.
.target은 "정점 인덱스, 오프셋" 목록입니다; 정점 = 베이스 + Σ 가중치 × 오프셋. 먼저 매크로 모프 (성별, 나이, 민족), 그 다음 세부 사항: 코, 입술, 광대뼈, 턱, 눈. MakeHuman 단위는 데시미터, Y 위쪽: Blender에서는(x, −z, y) × 0.1입니다. - 마커 없는 랜드마크. 모프가 가장 많이 움직이는 정점이 해당 모프의 랜드마크입니다 (입꼬리, 콧방울, 코끝). 이를 통해 동공 간 거리의 비율로 비율을 계산하고 참조와 비교하세요. 기억하세요: 가까운 카메라의 스크린샷은 원근 왜곡이 있습니다 — 정사영과 비교하지 말고 프레임에 맞게 카메라 (거리, 초점 거리)를 조정하세요.
- 프록시.
.mhclo는 프록시의 각 정점을 베이스의 세 정점에 바인딩합니다 (무게 중심 가중치 + 스케일의 오프셋). 형태가 변경될 때마다 그리고 각 표정 형태에 대해 프록시를 다시 계산하세요. - 표정. 표정 단위도 동일한
.target입니다. 이들로 ARKit 이름의 키를 조립하세요. 속눈썹과 눈썹은 피부를 따릅니다; 치아는 강체: 위 아치는 고정, 아래 아치와 혀는 강체로 회전합니다 (변환은 아래 아치에 대해 Kabsch 방법으로 찾음). 피부 오프셋을 치아에 전달하지 마세요 — 늘어납니다. - 목 절단 —
bmesh.ops.bisect_plane사용, 턱 아래 4~5cm: UV와 형태 레이어를 보존합니다. 원점은 절단면의 중심. - 아틀라스. 베이스의 UV는 전신을 위해 설계되었습니다. 아일랜드를 찾고, 얼굴 아일랜드를 회전시켜 위쪽 절반에 배치하고, 나머지는 아래에 패킹하고, 피부 텍스처는
map_coordinates를 통해 다시 계산하세요. 얼굴 특징이 아틀라스의 얼마나 많은 부분을 차지하는지 확인하세요 — 구강과 뒤통수가 그들과 경쟁해서는 안 됩니다. - LOD. Collapse-데시메이션; 형태 키는 가장 가까운 원본 정점의 오프셋을 전송하고, 강체 부분 (치아)의 경우 각 연결된 구성 요소에 대한 강체 변환을 통해 반환합니다. 데시메이션 후 축퇴된 면을 제거하세요.
수정 주기: 참조 옆에 세 대의 카메라로 렌더링 → config.json에서 한두 가지 변경 → 재조립. 이유를 가중치에서 변경하고, 메시를 패치하지 마세요.
Blender에서 스크립트 기반 처음부터 조립 (headless)
pip install bpy (Blender 5.2), numpy, scipy, scikit-image, pillow에서 검증됨. 렌더링 및 베이킹은 CPU의 Cycles. 미터, Z 위쪽, 얼굴은 −Y 방향, 턱은 z=0, 정수리는 z≈0.23; +X는 캐릭터의 왼쪽.
6개의 스크립트로 나누고 각각 후에 렌더링을 확인하세요 (PNG로 읽기): 01_sculpt → 02_lowpoly → 03_bake → 04_textures → 05_finish → 06_verify. 전체 재조립은 약 5분이 소요되므로, 결과를 패치하지 말고 이유를 수정하고 재조립하세요.
1. 거리장으로서의 조각
점 배열에 대한 함수 head_sdf(P), abs(x)를 통한 대칭. 프리미티브: 타원체, 구, 가변 반경 캡슐; 부드러운 합집합 smin(a,b,k) 및 차집합 smax(a,-b,k).
- 1차 덩어리, k≈0.02: 두개골 — 타원체 (중심 z=0.138, 반경 0.077/0.098/0.092), 얼굴 중간 및 아래 부분, 턱, 캡슐로 된 턱뼈 몸통과 가지, 볼 용적, 저작근.
- 2차, k≈0.013–0.018: 광대뼈, 광대뼈 아치, 캡슐로 된 눈썹 융기. 코는 눈썹 융기보다 깊게 시작해야 합니다, 그렇지 않으면 콧등 움푹 들어간 부분이 사라집니다.
- 눈: 안와를 빼고, 눈꺼풀 구를 추가하고 (r=0.015), 타원체로 틈을 자르고, 눈알을 삽입합니다 (r=0.0122, 중심 x=±0.032, y=−0.072, z=0.115).
- 코: 콧등, 코끝, 콧방울, 코기둥, 콧구멍 빼기. 입술 — 입술 사이와 폐쇄선의 홈이 있는 작은 k의 캡슐. 귀 — 회전된 좌표계의 플레이트에 조가비 컷아웃이 있음.
- 너비를 인체 측정과 비교하세요: 광대뼈 ~140mm, 턱 105–115mm, 눈꺼풀 틈 ~29×10mm, 동공 간 거리 ~64mm. 스무딩은 너비를 부풀립니다 — 매개변수가 아닌 결과를 측정하세요.
표면: skimage.measure.marching_cubes 사용, 1mm 그리드, 절반을 계산하고 대칭 이동. 메시 볼륨의 부호를 확인하세요 (양수여야 함): 뒤집힌 노멀은 검은 AO와 황록색 노멀 맵을 제공합니다.
2. 박스 모델링을 통한 리토폴로지
- 큐브 →
subdivide_edges(cuts=9, use_grid_fill=True)→ 면당 10×10 그리드. 머리 중심에서의 방향은 등각:tan(coord·π/4). - 고리: 패치에서
bmesh.ops.inset_region세 번. 눈 — 앞면의 2×2 패치, 입 — 4×2, 목 — 아래쪽에서 6×6 컷아웃. 패치 측면의 중간점이 눈과 입의 모서리가 됩니다. - 귀: inset →
extrude_face_region→ 두 번의 inset. - 눈꺼풀 두께와 입 주머니: 구멍 윤곽을 따라
extrude_edge_only; 주머니 뚜껑은 쿼드를 유지하기 위해 인접한 중간 정점을 수동으로 조립합니다. - 고리는 영역의 내부 경계에서 너비 우선 검색으로 표시합니다 (정점의 int 레이어), "작업 전후"가 아닙니다.
- 위치: 내부 고리는 곡선에 의해 명시적으로 지정됩니다 (눈꺼풀 틈은 눈꺼풀 구와 컷아웃의 교차점으로 수치적으로 계산됨; 입술 선은 정면 투영); 나머지 정점은 중심에서 표면까지의 광선, 그런 다음 30회 반복 "스무딩 + 투영".
- 이 거친 셀에서 언랩 수행: 이음새 — 뒤통수, 큐브 앞면 경계 (얼굴 아일랜드), 귀 밑면, 입술 선.
uv.unwrap(method='MINIMUM_STRETCH'), 얼굴 아일랜드 ×1.7, 입 주머니 ×0.45,pack_islands. - LOD: 한 번에 하나의 Catmull-Clark 레벨 (
uv_smooth='PRESERVE_BOUNDARIES'), 각 레벨 후 조각에 맞춤. 셀 = LOD2, +1 레벨 = LOD1, +2 = LOD0; UV는 모두 공통.
레벨 맞춤:
- 주요 덩어리 — 정점 노멀을 따라: ±T (첫 번째 레벨의 경우 16mm, 두 번째 레벨의 경우 7mm) 세그먼트에서 가장 가까운 부호 변화를 찾습니다;
- 코 — 정면 높이 맵으로: x와 z는 유지, y는 정면 투영으로 취함;
- 귀 — 4mm 제한으로 그라디언트를 따른 투영;
- 눈꺼풀과 입술 윤곽 — 곡선으로 되돌리고, 체인의 정점을 순서대로 균일하게 배치;
- 마지막에 각 정점을 대칭 쌍과 평균화 (cKDTree).
3. 베이킹
Triangulate 수정자 (SHORTEST_DIAGONAL)가 있는 LOD0의 복사본 — 이것을 베이킹하고 내보냅니다. scene.render.bake: use_selected_to_active, cage_extrusion=0.004, max_ray_distance=0.009, margin=16. 맵: NORMAL (4 샘플), AO (24), 플러스 margin=0인 float 이미지의 가장 낮은 폴리곤 메시의 POSITION — 이것은 마스크를 위한 "텍셀 → 3D 포인트" 맵입니다.
4. numpy의 텍스처
위치 맵에서 마스크 계산: 입술 — 입술 가장자리 곡선 사이 (메시 고리와 일치), 눈썹 — 눈썹 융기의 띠, 색상 영역 — 좌표에 따른 가우시안 및 계단, 노이즈 — 난수 그리드에 대한 삼선형. 보이지 않는 내부 (입 주머니, 눈꺼풀 안쪽)는 필드의 부호로 결정되고 평평한 노멀을 얻습니다. 아일랜드 주변 영역은 가장 가까운 덮인 텍셀로 채웁니다 (distance_transform_edt with indices).
5. 머티리얼, 셰이프, 내보내기
- Principled BSDF: BaseColor (sRGB), ORM (Non-Color) → Separate Color → G in Roughness, B in Metallic, R in
glTF Material Output그룹 노드 및Occlusion입력; Normal → Normal Map. use_backface_culling = True, 그렇지 않으면 glTF가 doubleSided를 얻습니다.- jawOpen — 관절 (y=0.010, z=0.096) 주위 축을 중심으로 아래턱 영역을 13° 회전, 입술 그룹의 가중치 사용; 깜빡임 — 구를 따라 위 눈꺼풀 미끄러짐.
- 셰이프 키 생성 후 값을 0으로 설정하세요. 그렇지 않으면 파일이 입을 벌리고 눈을 감은 상태로 열립니다.
- GLB:
export_morph=True, export_tangents=True, use_selection=True. 눈에도 LOD가 필요합니다.
6. 검증 — 필수 및 독립적
내보낸 GLB를 빈 씬에 가져와 측정: 삼각형, 미터 단위 치수, 머티리얼 및 텍스처 크기, 셰이프 이름 및 가져올 때 값 (0이어야 함), 축퇴된 면, 대칭 쌍이 없는 정점, UV가 0–1 범위 내, 베이킹된 적용 범위에 대한 UV 삼각형 면적 합계 (차이는 겹침을 의미).
자체 확인 후 파일을 독립적인 비평가 — 프로세스를 보지 못한 별도의 에이전트에게 전달하고, GLB를 직접 열고 README를 신뢰하지 말라고 요청하세요. 작성자의 렌더링은 확인을 대체하지 않습니다: 작성자는 렌더링 전에 셰이프를 0으로 설정하고 파일에 1로 있는 것을 보지 못합니다.
이미 밟은 함정
-
참조 없이 작업. 첫 번째 머리가 바로 그 이유로 '마네킹'이 되었습니다; 참조와 기본 메시가 첫 번째 단계입니다.
-
피부색은 렌더링이 아닌 텍스처로 확인하세요: 참조 스크린샷은 조명된 색상을 보여주고, 과다 노출된 렌더링은 반대 방향으로 거짓말을 합니다. 색조는 작은 단계로 변경하세요.
-
모프를 추가할 때 입 안감이 뺨과 턱을 뚫고 나옵니다. 각 형태에 대해 외부 피부의 BVH로 정점을 확인하고 1.5mm 움푹 들어가게 하세요. 안감 아일랜드는 면 수가 아닌 UV 위치로 찾습니다.
-
프록시에서 오른쪽 키가 살아있는 왼쪽 키로 비어 있을 수 있습니다. 누락된 키를 대칭으로 구축하고 이동이 0.5mm 미만인 키는 버리세요. 자동 확인: 액세서리 아래의 피부를 0.5mm 이상 움직이는 모든 머리 형태는 액세서리에 있어야 합니다.
-
데시메이션 후 가장 가까운 정점으로 형태를 전송하면 치아가 닫혔을 때 위턱과 아래턱 치아가 혼동됩니다. 강체 부분의 모서리 길이 변화를 확인하세요 — 0이어야 합니다.
-
눈썹 기울기 모프는 명확하게 명명되지 않았으며 밀리미터를 움직입니다. 돌리기 전에 모프가 안쪽 끝과 바깥쪽 끝을 어디로 이동하는지 계산하세요. 찡그린 중립은 기본 형태에 대한 표정 단위의 기여로 치료합니다.
-
LOD에 입 벌림이 포함된 경우 혀와 치아가 있어야 합니다.
-
FBX는 ORM을 전달하지 않습니다: README에서 맵이 엔진에서 수동으로 할당된다고 말하세요. 머리카락은 블렌드가 아닌 알파 마스크를 설정하세요.
-
다른 사람의 텍스처에서 머리카락은 사용되지 않은 영역에 로고가 있는 경우가 있습니다: UV 삼각형이 닿지 않는 텍셀을 지우세요.
-
README를
verification.json에 대해 한 줄씩 확인하세요: "키가 모든 부분에 있음" 및 "축퇴된 면 0개"는 비평가가 반증했습니다. 이는 머리에 대해서만 사실이었기 때문입니다. -
삭제 후 bmesh 정점 인덱스는 안정적이지 않습니다: 새 정점이 비어 있는 슬롯을 차지합니다. "새 정점 → 원본" 연결은 BMVert에 대한 참조로 저장하고 인덱스는 맨 마지막에 가져오세요.
-
bmesh.ops.extrude_face_region은 원본 면을 남깁니다 — 제거해야 합니다. 그렇지 않으면 비다양체입니다.
…
[번역을 위해 잘림]