kidstoon — 턴어라운드 시트 → 3D 캐릭터 작업 매뉴얼 (TRELLIS v1)
유사작업 매뉴얼. 시작 전에 이 문서를 읽고 순서대로 실행한다. 시행착오로 다시 헤매지 않기 위해 만들었다. 규칙 =
rules.md· 상태 =handoff.md· 설계근거 =design.md. 최초 작성 2026-08-23(코디). 실측 근거는 전부 이 날 집PC(RTX 4070 Ti SUPER 16GB)에서 재현한 것이다.
0. 이 파이프라인이 하는 일
2D 턴어라운드 시트 4컷(정면·좌측면·우측면·후면) → 텍스처 입힌 3D 메시(GLB) → Unity Humanoid 리깅.
핵심 구조 = 형상과 색을 다른 입력으로 만든다. 한 입력으로 둘 다 얻으려 하면 반드시 한쪽이 깨진다(§2 실측표). 형상은 rembg 경로를 그대로 두고, 색은 생성 뒤 원화를 직접 투영해 굽는다.
원화 4컷
├─(A) rembg 알파 그대로 + 스케일 정규화 ──→ run_multi_image ──→ 메시(형상)
└─(B) 원본 RGB + 구멍메운 마스크 + 같은 정규화 ──→ bake_texture ──→ 텍스처(색)
↓
GLB → retopo → Unity Humanoid
1. 환경 (한 번만)
| 항목 | 값 |
|---|---|
| 실행 머신 | 집PC(4070 Ti SUPER 16GB). python C:/dev/tools/gpubox/gpubox.py {wake,run,off} |
| TRELLIS | IgorAherne/trellis-stable-projectorz → D:\trellis (venv) |
| 설치 | kidstoon/tools/trellis_install.bat — 프리빌트 휠이라 CUDA 툴킷 불필요 |
| torch | 2.7.0+cu128 고정(휠 ABI. 최신 받으면 설치는 되고 import 에서 죽는다) |
| 작업 파일 | 미니PC에서 편집 → gpubox.py push <로컬> <원격> → 집PC에서 실행 → g.fetch 로 회수 |
| 소요 | 모델 로드 40~190초 · 생성 6~13초 · bake 8초 · peak VRAM 4.8GB(half) |
2. 🔴 절대 하지 말 것 (전부 실측 근거)
판정 지표 = 턴테이블 12뷰의 min(실루엣 폭)/max(실루엣 폭). 입체 0.4~0.83 · 판때기 0.1 이하.
trellis_gen.py 가 [shape] 줄로 자동 출력한다.
| 하면 안 되는 것 | 폭비율 | 결과 |
|---|---|---|
rembg-fill — 이진화·페더·연결성분 필터 후 구멍메움 |
0.035 | 판때기 |
배경색·연결성으로 직접 컷아웃해 입력 (edge) |
0.030 | 판때기 |
배경만 다른 색으로 바꿔 RGB 로 입력 (bgswap) |
0.026 | 판때기 |
배경 교체 후 rembg 로 알파를 새로 뽑아 입력 (rembg-swap) |
0.016 | 판때기(최악) |
| (대조) 원본 RGB 그대로 | 0.701 | 정상 |
| (대조) rembg 알파 그대로 RGBA 전달 | 0.701 | 정상(위와 동일 메시) |
✅ rembg-raw + 스케일 정규화 |
0.828 | 형상 최고점 |
✅ rembg-holes + 정규화(소프트매트 보존·닫힌 구멍만) |
0.817 | 정상 — 흰비율 0.234→0.257 |
- seed 1/2/3 · multidiffusion · 3컷 전부 같은 결과. 운이 아니라 100% 재현된다.
- 🔑 금지의 진짜 경계는 「구멍을 메웠는가」가 아니라 「외곽 실루엣을 건드렸는가」다(2026-08-23 정정).
rembg-fill이 붕괴한 건 구멍을 메워서가 아니라 이진화·페더·연결성분 필터로 외곽 매트를 다시 그렸기 때문이다.rembg-holes는 소프트 매트를 손대지 않고 테두리에 안 닿는 구멍만 255 로 채우므로 붕괴하지 않는다(0.817). - ⚠ 단 앞발은 그 경로로도 안 살아난다 — 앞발은 닫힌 구멍이 아니라 실루엣 결손(외부와 연결)이라
binary_fill_holes가 닿지 못한다. 실측 구멍복구 = front 2,554px(이마·가슴 일부)뿐. §5 「앞발」 참조. - 🔴 흰 부위를 실루엣 안으로 넣으면 방식 불문 붕괴한다(2026-08-23 3방식 실측).
rembg-swap은 전경내 흰픽셀을 2,554 → 44,269px 로 끌어올렸지만(=앞발·가슴 전부 포함) 형상은 0.016 으로 최악이었다. 실루엣에 흰 부위를 넣는 시도는 여기서 끝낸다 — 이 줄이 그 종결 근거다. - ⚠ 정면 렌더 한 장만 보고 판단하지 마라. 판때기도 정면은 멀쩡하다. 반드시
[shape]수치를 본다. - 정밀도(half/full)·샘플러 모드(stochastic/multidiffusion)·스텝(12/25)은 지렛대가 아니다 — 0.66~0.71 로 기본값(half·stochastic·12)보다 낫지 않았다. 품질이 안 나올 때 이 노브부터 돌리면 시간만 태운다.
bake 쪽 금지 항목
- 🔴 simplify=False 로 굽지 마라. UV chart 가 과밀해지고 Unity 폴리곤 예산도 초과한다.
🔑simplify_ratio 는 「남길 비율」이 아니라 「제거할 비율」이다 — postprocess_mesh docstring 은
"keep" 이라 적었지만 실제 호출은 PyVista.decimate(ratio) 이고 to_glb 문서는 "remove" 규약이다.
즉 0.95 = 95% 제거. 문서를 믿지 말고 감면 후 실제 triangle 수를 찍어 확인한다.
- 🔴 bake_texture(mode='fast') 금지 — 여러 관측을 순회하면서도 masks[0] 만 참조한다(다뷰 bake 불가).
- 🔴 opt loss 가 떨어진 것만으로 bake 성공을 판정하지 마라. loss 는 카메라 투영 텍스처 샘플의 손실일 뿐이고,
마지막 UV 아틀라스 점유율은 검증하지 않는다. 실측에서 loss 0.507→0.14 인데 텍스처는 전부 검정이었다.
- 🔴 원화 직접 bake 결과에 알베도 gain 을 다시 적용하지 마라(이중 보정). 코드가 sheet bake 분기에서 gain=1.0 으로 리셋한다.
3. 단계별 절차
S1. 턴어라운드 시트 확보
- 컷 =
front·side(치즈면) ·side-tabby(태비면) ·rear. 도구 =kidstoon/tools/gen_turnaround.py. - 등신 보정 =
fix_ratio.py(자동 호출). 상세·함정 =rules.md. - 검증 = 4컷 모두 3.0등신 · 무지 배경 · 그림자 없음 · 컷 간 의상/무늬 일관.
S2. 두 갈래 입력 만들기 — cutout_alpha.py
:: (A) 형상용 — 소프트매트 보존 + 닫힌 구멍만 채움 + 스케일 정규화
python cutout_alpha.py --images <4컷> --out <dir>\cut_i --mode rembg-holes --norm-height 880
:: (B) bake 용 — 원본 RGB + 구멍메운 마스크, 같은 정규화
python cutout_alpha.py --images <4컷> --out <dir>\cut_h --mode rembg-fill --norm-height 880
- (A) 는
rembg-holes가 기본이다(2026-08-23 변경).rembg-raw는 형상 점수가 0.828 로 조금 높지만 이마·가슴에 뚫린 알파 구멍이 그대로 3D 에 검은 공동으로 남는다.rembg-holes는 외곽을 안 건드리고 그 구멍만 메워 형상 0.817 · 흰비율 0.234→0.257 · 앞섶 육안 개선을 얻는다. --norm-height 880 --canvas 1024= 캐릭터 높이를 880px 로 맞춰 1024² 중앙 배치. 컷 간 스케일이 어긋나면 모델이 그 모순을 평균내며 뭉갠다(멀티뷰 실전 지침). 정규화는 형상도 개선했다(0.701→0.828).- 검증 = 출력 로그의
캐릭터높이가 4컷 모두 880px ·크기 1024x1024· (B)는전경내 흰픽셀이 (A) 대비 크게 늘어야 한다(실측 front 2,927→44,252px).
S3. 형상 생성 + 원화 리텍스처 — trellis_gen.py
python trellis_gen.py --images <cut_g 4장> --sheet-dir <cut_h> --out <out>
- 형상 =
run_multi_image(mode=stochastic, seed=1, ss/slat steps 12, cfg 7.5/3) - 색 =
bake_texture(mode='opt', lambda_tv=0.0)에 원화 관측을 넣는다. - 🔴
mode='fast'금지 — 모든 뷰에서masks[0]만 참조하는 버그가 있다(다뷰 bake 불가). lambda_tv=0.0— 태비 줄무늬 같은 얇은 선을 평활화하지 않기 위해.- 카메라 정합은 자동 탐색한다: fov {6,8,10,14,20,30} × 상하반전 {F,T} 를 실루엣 IoU 로 고른다.
r은obj_h*res/(2*목표픽셀높이*tan(fov/2))로 역산. 원화는 정투영이고bake_texture는 원근만 받으므로 작은 fov 로 근사하는 것이다(실측 채택값 fov=6.0, r=11.1, IoU 0.816). - 🔴 그래서
far를 반드시 같이 넘긴다(far=max(100, r*4)).bake_texture기본 far 는 10.0 이라 r=11.1 짜리 카메라를 통째로 잘라낸다 — 그게 「구운 텍스처가 검다」의 정체였다(§8). - 검증(스크립트가 자동 출력·assert)
| 로그 | 기준 |
|---|---|
|
[shape] 비율| ≥ 0.4 (미만이면 형상 실패 — S2 (A) 를 의심). 실측 0.817 | |[retex] 관측∩메시| 0.85~0.95. 낮으면 메시가 관측을 못 덮는 것(앞발·꼬리) | |[retex] 실루엣IoU| ≥ 0.55 (미만이면 assert 로 중단). 실측 0.817 | |[uv-audit] coverage| 0.55~0.62. 0 이면 UV raster 실패 → assert | |[uv-audit] raw_nonblack| > 0.15 — inpaint 이전 값이라 이게 진짜 판정값. 실측 0.654 | |[retex] 텍스처 비검정 비율| > 0.15. 실측 0.882 | |[retex] 흰비율| > 0 (0 이면 흰 가슴이 여전히 죽은 것). 실측 0.257 |
S4. 일치도 게이트 — gate_master.py
python tools/gate_master.py <out>/gate-right.png <out>/gate-left.png <out>/gate-front.png
- 뷰 매핑(실측) = yaw 180=정면 · 090=치즈면 · 270=태비면 · 000=후면.
파일명에
left/right토큰이 있어야 볼 비대칭 검사가 동작한다. - 통과선 = 0.80. 자체검사(마스터를 렌더 자리에 투입) = 1.0000 이어야 게이트가 정상이다.
- 통과 못 하면 §5 의 「알베도」부터 본다.
S5. Unity 로 넘기기 (미검증 — 아직 실행 안 함)
- 웹조사 기준: remesh(retopo) → UV·텍스처 재생성 → Unity 순서. 폴리곤 예산 PC ~50K faces.
- AI 메시는 삼각·불규칙 토폴로지라 관절에서 찢어진다 → Humanoid 리깅 전 retopo 필수.
4. 파라미터 기준표
| 구간 | 파라미터 | 값 | 근거 |
|---|---|---|---|
| 형상 입력 | --mode / --norm-height / --canvas |
rembg-holes / 880 / 1024 |
0.817·흰비율 0.257 (raw 는 0.828·0.234) |
| bake 입력 | --mode / 정규화 |
rembg-fill / 형상과 동일 |
높이·중심이 같아야 정합된다 |
| 형상 | precision / mode / steps | half / stochastic / 12·12 | 다른 조합이 더 낫지 않았다 |
| 형상 | cfg (ss / slat) | 7.5 / 3 | 포크 기본값 |
| 메시 | --simplify |
0.90 (= 90% 제거) | faces 20,650 = Unity LOD0 예산 |
| 메시 | fill_holes | true · 0.04 · nbe=int(250·√(1−simplify)) · 1024 · 1000뷰 |
to_glb 와 같은 공식 |
| bake | mode / lambda_tv / steps | opt / 0.0 / 2500(고정) |
fast 는 masks[0] 버그 |
| bake | --texture-size |
2048 | 1024 와 게이트 차이 미미, UV coverage 0.59 로 여유 |
| bake | near / far | 0.1 / max(100, r*4) |
🔴기본 far=10 이 r=11.1 을 잘라낸다(§8) |
| bake | fov / r | 자동탐색. 출시 기준 IoU ≥ 0.80 | 코드 하한 0.55 · 실측 0.817 |
| 렌더 | 알파 | 검정·흰 두 배경 역산 | 단색 1회 렌더는 반투명부에 배경색이 밴다 |
| 알베도 | --target-medv |
gaussian 경로 0.53 / 원화 bake 는 0(끔) | 이중 보정 방지 |
| 게이트 | 폭비율 / total | 0.40~0.83 / ≥ 0.80 | 실측 0.817 / 0.8280 PASS |
Unity 폴리곤 예산(미검증, 운영 기준) — Humanoid LOD0 15k~30k tris · LOD1 8k~15k · LOD2 3k~6k.
--simplify 0.90 실측 = verts 12,572 · faces 20,650 → LOD0 구간에 그대로 들어간다.
목표 tris 를 바꾸려면 simplify = 1 - 목표/원본면수.
5. 증상별 대처
| 증상 | 원인 | 대처 |
|---|---|---|
| 90도 돌리면 종잇장 / 후면에 정면 얼굴 | 모델 입력 실루엣을 건드렸다 | S2 (A) 를 rembg-raw 로 되돌린다 |
| 가슴·앞발·이마가 검은 공동 | rembg 가 흰 부위를 배경으로 오려냄 | 정상. bake 단계에서 원화로 덮는다(S3) |
| 전체가 원화보다 어둡다 | TRELLIS 가 균일하게 어둡게 굽는다(medV 0.41 vs 0.53) | 알베도 정규화 gain(자동) |
| 게이트는 통과인데 눈으로 이상 | 게이트는 3뷰만 본다 | orbit.mp4 육안 + [shape] 수치 병행 |
| 텍스처가 통째로 검정 | far 기본값 10.0 이 카메라를 잘랐다(§8) |
far=max(100, r*4) 를 넘긴다. [uv-audit] raw_nonblack 이 판정값 |
| 🔴앞발이 흰색이 아니라 갈색·검정 | rembg 가 흰 앞발을 배경으로 오려내 형상 자체에 앞발이 없다. 텍스처로는 못 고친다 — 그 자리엔 구울 표면이 없어 inpaint 가 소매색을 번지게 한다 | 현재 미해결. 실루엣에 넣는 3방식 전부 붕괴(§2). 대안 = ①캐릭터에 주황 장갑을 씌워 배경과 대비를 준다(장화와 세트라 디자인상 자연스럽다 · 페페 결정 사안) ②§9 전환 경로 |
6. 도구 목록 (C:\dev\kidstoon\tools)
| 파일 | 역할 |
|---|---|
trellis_install.bat |
집PC TRELLIS 설치(프리빌트 휠) |
cutout_alpha.py |
입력 4모드(rembg-raw / rembg-fill / bgswap / edge) + 스케일 정규화 |
trellis_gen.py |
형상 생성 + 평면검출 + 알베도 정규화 + 원화 리텍스처 |
gate_master.py |
원화 일치도 게이트(deskpet compare_master.py 재사용) |
trellis_diag_preprocess.py |
TRELLIS 가 실제로 보는 입력을 덤프(문제 진단 1순위) |
trellis_*_sweep.bat |
노브·입력 변형 실측 스윕 |
7. 한계와 대안 (조사 결과)
- TRELLIS.2 는 후보가 아니다 — 공식 요구 24GB·Linux, 다중이미지는 미병합 PR이며 현 포크와 같은 평균/순환 융합.
- 형상 품질을 더 올리려면
tencent/Hunyuan3D-2mv(멀티뷰 전용 finetune, canonical front/left/back API)가 후보다. shape only 로만 쓴다(16GB. 텍스처 생성기까지 켜면 29GB 필요). 색은 이 매뉴얼의 원화 bake 를 그대로 쓴다. to_glb가 만드는 것은 UV+PBR 머티리얼뿐이다. armature·스킨웨이트는 없다 — Unity Humanoid 는 별도 작업.
7-1. 상하(flip) 규약 — ⚠2026-08-24 정정
🔴아래 3번이 틀렸었다. 「bake 직전 pre-flip 은 맞다, 지우지 마라」가 R3' 의 범인이었다. 정정 근거·증거는 §10.
- 파일 원화는 PIL/NumPy 로 그대로 읽는다.
- IoU 탐색 결과가
flip_obs=True일 때만obs/masks를 먼저 뒤집는다. - ~~
bake_texture호출 직전 항상np.flipud해서 넘긴다~~ → 넘기지 마라. 2단계 결과를 그대로 넘긴다. flipud 를 태우면 아틀라스가 표면 기준 상하 반전으로 칠해진다. - bake 가 돌려주는
tex은 GLB 에 그대로 넣는다(trimesh 가 export 때 v 를 뒤집어 규약이 맞는다). nvdiffrast 턴테이블에 넣을 때만np.flipud(tex)— 단 그 턴테이블은 GLB 와 상하가 반대로 나온다 (원인 미규명, 실측). 그래서 턴테이블은 bake 진단용이고 게이트 이미지는 GLB 에서 직접 렌더한다.
flip=False 로 IoU 가 이겼다는 것은 「파일 원화 마스크가 rast['mask'] 와 같은 상하 규약」이라는 뜻이다.
⚠단 치비 실루엣은 상하가 거의 대칭이라(질량중심 0.514 vs 0.513) IoU 로는 상하를 못 가른다 — 이 탐색을
상하 규약의 근거로 쓰지 마라.
8. ✅해소 — 텍스처 검정의 정체는 far 였다 (2026-08-23)
증상이던 것 = 카메라 정합 정상(IoU 0.816) · 최적화 정상 수렴(loss 0.507→0.14)인데 반환 텍스처 비검정 0.000004.
🔑원인 = bake_texture 의 기본 far=10.0. 원화가 정투영이라 fov 6도로 근사하면 카메라가
r≈11.1 로 멀어지는데, far 를 안 넘기면 메시가 far plane 너머라 내부 래스터가 통째로 빈다
→ 모든 픽셀이 texture(0,0) 한 점을 샘플링 → 그 한 점만 관측 평균색으로 수렴한다.
loss 가 떨어진 건 그래서고, 비검정 0.000004 = bilinear 이웃 4픽셀이었다.
우리 IoU 탐색 코드는 far=100 을 쓰고 있어서 정합은 맞는데 결과만 검은 모순이 생겼다.
수정 = bake_texture(..., near=0.1, far=max(100.0, r*4)) 한 줄. 비검정 0.000004 → 0.870.
- ❌용의자였던
simplify·texture_size는 무죄다. 기준 성공조합(0.95/1024)에서도 똑같이 검었다 (coverage 0.601 · raw 0.000004). 노브부터 돌렸으면 또 하루를 태웠을 자리다. - 📌교훈 = 렌더 경로가 둘이면 near/far 를 한 곳에서 정해 양쪽에 같이 넘긴다. 검증 래스터와 실제 bake 가 다른 클립 평면을 쓰면 검증이 통과하는데 산출물만 틀어진다.
UV 아틀라스 감사 — 계속 켜 둔다
bake_texture 는 loss 만 보고하고 최종 UV raster 를 검증하지 않는다. TRELLIS 소스는 안 건드리고
cv2.inpaint 를 가로채 세 값을 찍는다(trellis_gen.py 에 구현됨. 덤프 = debug_uv_chart.png ·
debug_texture_pre_inpaint.png).
| 값 | 정상 | 벗어나면 |
|---|---|---|
coverage (UV raster 점유율) |
0.55~0.62 | 0 = UV raster 실패 → 감면비율을 올려 재생성(텍스처 크기 문제 아님) |
raw_nonblack (inpaint 이전) |
> 0.6 | 0 = 최적화가 색을 못 만든 것 → 클립 평면·카메라·관측을 본다 |
after (inpaint 이후) |
> 0.85 | raw 는 큰데 0 = inpaint 가 지운 것 |
두 assert 가 코드에 박혀 있어 검은 텍스처가 GLB 까지 가지 못한다.
함께 고친 것
- opt loss 가 메시 밖 관측 픽셀까지 학습하던 문제 → 관측 마스크를 IoU 탐색이 이미 만든 래스터 마스크와 AND 해서 넘긴다. 실측 관측∩메시 = 0.86~0.95(나머지 5~14%가 앞발·꼬리 = §5).
- 게이트 별칭
gate-{front,left,right}.png를 생성 단계에서 자동으로 만든다(뷰 매핑 실수 방지).
그 밖
- ⏳ Unity 반입(S5)은 아직 한 번도 안 해봤다 — 전부 미검증.
- ⚠
utils3d는 설치 스크립트가 commit pin 없이 받아온다 → 환경이 바뀌면 UV 감사 값을 다시 본다.
9. 이 경로를 포기해야 할 때
아래 한계가 출시 기준을 넘으면 TRELLIS 를 더 반복하지 말고 경로를 바꾼다.
- 2D 턴어라운드는 배면 두께·관절 내부·발바닥·꼬리 단면의 실제 3D 정보를 주지 않는다(모델이 추정한다).
- opt bake 는 원화의 조명·그림자까지 색으로 굽는다 — 재조명 가능한 PBR 재질이 아니다.
- 4뷰가 서로 모순되면 L1 최적화는 UV seam 과 평균색을 만든다.
- GLB 에는 Humanoid bone·weight·blendshape 가 없다. GLB 생성 = 리깅 완료가 아니다.
🔀 전환 경로 = 툰 휴머노이드 베이스 메시를 수동 retopo·리깅한 뒤, 4뷰를 카메라 프로젝션 텍스처로 굽는다. 흰 가슴·앞발처럼 rembg 가 구조로 오인하던 영역도 이 경로에서는 정상 보존된다.
10. ✅해소 — R3' 「Blender 가 텍스처를 섬 단위로 뒤섞는다」의 정체 (2026-08-24)
증상이던 것 = GLB 를 Blender 가 읽으면 얼굴이 배에 거꾸로 붙는다. 게이트 턴테이블은 멀쩡(0.828 PASS).
전역 U/V/180° 변환 4종(→재시험 8종) 전부 불일치. process=False 재수출도 동일.
🔑정체 = 섬 뒤섞임이 아니다. 아틀라스 「내용」이 표면 기준 상하 반전으로 칠해진 것이다.
bake_texture 에 관측을 np.flipud 해서 넘기는 바람에, 머리 표면에는 장화 색이, 발 표면에는 얼굴 색이
칠해졌다. 섬 배정(UV↔아틀라스 짝)은 처음부터 정상이었다. 그래서 렌더가 「깨진 그림」이 아니라
「말끔한 거꾸로 고양이」로 나왔고, 그게 사람 눈에 「섬이 뒤섞였다」로 읽혔다.
수정 = pre-flip 제거 한 줄(§7-1 정정). 행프로파일 상관 as-is 0.287 → 0.926.
왜 하루를 태웠나 — 진단을 막은 함정 3개
- 🔴게이트 턴테이블이 동어반복이었다. bake 결과를 bake 가 최적화한 그 래스터로 되비추므로 정의상 원화를 재현한다. 그래서 아틀라스가 어떻게 칠해졌든 게이트는 통과한다. 실제로 수정 후엔 GLB 가 정상이 되고 게이트 쪽이 거꾸로 나왔다(상관 as-is 0.243 / flip 0.914) — 두 경로가 상하 반대라는 게 이때 드러났다. 판정은 납품물(GLB)로 해야 한다.
- 🔴치비 실루엣은 상하가 거의 대칭이다(질량중심 0.514 vs 0.513, 상·하 25% 면적 1.5% 차). 그래서 ①실루엣 IoU 탐색이 상하를 못 가르고 ②「거꾸로 고양이」가 실루엣만으론 정상으로 보인다. ⚠실루엣 IoU 를 상하 규약의 근거로 쓰지 마라. 옆모습은 주둥이(높이 0.64)·꼬리(0.04)가 있어 상하를 잘 가른다(IoU 0.976 vs 0.588) — 상하를 봐야 하면 옆모습을 봐라.
- 🔴전역 UV 변환으로 안 풀린다는 사실을 「임포터 탓」으로 읽었다. 정반대다 — UV 로 안 풀리면 UV 가 아니라 내용이 틀린 것이다. 지문을 이렇게 읽어라.
도구 — tools/glb_cpu_render.py (신설)
trimesh·Blender·GPU 없이 GLB 바이트를 직접 struct 파싱해 정투영 래스터하는 순수 numpy 렌더러.
이번 진단의 유일한 비순환 판정자였다(파이프라인의 어떤 규약도 공유하지 않는다).
_selfcheck() 로 자기 규약(기하 +Y=위, UV top-left)을 먼저 증명한 뒤 판정에 썼다 — 판정자부터 검증해라.
| 쓸 곳 | 명령 |
|---|---|
| GLB 육안 확인 | PYTHONUTF8=1 python tools/glb_cpu_render.py <glb> --out <prefix> |
| 규약 자체검사 | python -c "import glb_cpu_render as g; g._selfcheck()" |
남은 미규명
- nvdiffrast 턴테이블과 glTF 가 왜 상하가 반대인지는 못 밝혔다(기하 방향은 실루엣상 일치인데
칠만 반대다). 실용상 문제 없다 — 게이트를 GLB 직접 렌더로 바꿔 경로를 하나로 만들었다.
trellis_gen.py가 매 실행 두 경로 상관을 찍는다([verify] 턴테이블↔GLB). flip 쪽이 커야 정상.