반응형
three.js 배치·JSON·스크린샷을 활용해 스타일별 이미지를 뽑아내려는 사람을 위한 솔직한 가이드
실제로 해보면 금방 막힙니다. three.js로 방과 가구를 배치하는 건 잘 되는데, 그걸 근거로 “레이아웃은 그대로 유지하면서” 스타일만 달리한 실사 이미지를 얻는 건 생각보다 난이도가 높습니다. 아래는 제가 겪은 핵심 난관과, 그걸 줄이기 위한 현실적인 설계 방법을 단계별로 정리한 글입니다. 임베딩, 학습, 파인튜닝까지 고려했을 때 어디서 어렵고 무엇을 선택해야 하는지도 함께 짚습니다.
왜 어려운가 — 본질적인 난관 7가지
- 레이아웃 보존 vs. 스타일 자유도의 충돌
텍스트 프롬프트는 “느낌”을 잘 바꿔주지만, 가구 위치·크기·벽체 같은 구조 보존은 못합니다. 반대로 구조 제어(Depth, Edge, Segmentation 등)를 강하게 걸면 스타일 변화가 빈약해지기 쉽습니다. 둘 사이의 최적점을 찾는 게 핵심 난이도입니다. - 입력 소스가 이질적
우리는 JSON(정확한 수치), 캡처 이미지(시각적 단서), 텍스트 프롬프트(의도)라는 세 가지 다른 표현을 한 장의 사진으로 합성해야 합니다. 모델은 숫자를 직접 이해하지 못하니, JSON은 결국 마스크/가이드 맵 같은 “이미지형 조건”으로 변환해야 합니다. - 카메라·광원·재질의 불안정성
three.js 캡처는 대부분 게임 같고 플랫합니다. FOV, 화각, 노출, 그림자가 실사와 다르면, 생성 모델이 “실사로 보정”하려다 레이아웃을 망가뜨리기도 합니다. - 스케일 기준의 부재
침대가 실제보다 작거나 크게 나오면 전체 감이 무너집니다. JSON에 치수가 있어도, 모델이 그걸 직접 쓰지 못하니 깊이 지도(Depth) 같은 간접 표식으로 스케일을 암시해야 합니다. - 참조 일관성 문제
같은 배치라도 스타일만 바꾼 이미지들을 연속으로 뽑으면, 침대 모양·문짝 위치가 바뀌는 일이 흔합니다. 시드 고정, 컨트롤 신호 고정, 임베딩/LoRA를 섞어 일관성을 유지해야 합니다. - 데이터·비용·시간의 삼각 딜레마
파인튜닝을 하면 좋아질 때가 있지만, 그 전에 데이터 수집·정제·라벨링이라는 큰 산이 있습니다. 비용과 시간의 한계 때문에 “프롬프트+ControlNet” 선에서 최대치 성능을 끌어내는 전략이 자주 필요합니다. - 파이프라인의 “끈기” 요구
실험이 한두 번에 끝나지 않습니다. 프롬프트/강도/컨트롤 채널/업스케일러를 바꿔가며 수십 번 조정해야 합니다. 자동화 스크립트와 캐시가 없으면 금방 지칩니다.
전체 설계도 — 가장 많이 쓰는 현실적 파이프라인
입력
- scene.json : 방 크기, 가구 위치/치수, 카메라 파라미터
- capture.png : three.js에서 동일 카메라로 캡처한 저품질 스냅샷
- style : “북유럽 미니멀”, “모던 럭셔리” 같은 스타일 토큰
전처리
- 캡처 → Depth/Edge/Segmentation 맵 추출 (ControlNet용)
- JSON → 2D 평면도/마스크 렌더 또는 레이블된 레이아웃 맵 생성
예: 벽(1), 바닥(2), 침대(10), 데스크(11) 등 클래스 인덱스 이미지
생성(1차)
- ControlNet-Depth + ControlNet-Canny/MLSD(직선) 두 채널 병용
- 프롬프트는 “레이아웃 보존”을 1문장으로 강하게 명시, 나머지는 스타일 설명
- Guidance(또는 CFG)와 Control strength를 0.6~0.9 구간에서 스윕
정제(2차)
- 업스케일/리라이트 모델로 디테일 강화
- 필요 시 소규모 인페인팅으로 어색한 부분만 보정
일관성 유지
- 시드 고정, 동일 컨트롤맵 재사용
- 특정 스타일의 텍스트 임베딩 / LoRA를 고정적으로 적용
임베딩·학습·파인튜닝, 언제 쓰고 무엇이 달라지나
- 텍스트 임베딩(Style/Concept Embedding)
- 목적: “북유럽 미니멀” 같은 스타일을 문장 대신 고정된 벡터로 재현성 있게 호출
- 데이터: 스타일별 참고 이미지 20~100장 수준으로도 시도 가능
- 장점: 가볍고 빠름, 프롬프트 글자 수에 덜 민감
- 한계: 구조 보존 문제는 여전히 ControlNet/깊이맵에 의존
- LoRA(경량 파인튜닝)
- 목적: 특정 재질·마감·조명 톤을 가볍게 학습해 일관적 스타일 확보
- 데이터: 수십~수백 장, 클래스 다양성보다 톤·재질의 일관성이 중요
- 장점: 스타일 일관성 향상, 프롬프트 간소화
- 한계: 데이터 편향이 생기면 가구가 바뀌거나 과적합
- 풀 파인튜닝(모델 자체 재학습)
- 목적: 레이아웃+스타일을 모두 우리 도메인에 최적화
- 데이터: 수천 장 이상, 어ノテ이션(레이아웃/세그멘테이션)까지 있으면 좋음
- 장점: 최고의 일관성과 재현성
- 한계: 비용·시간·인프라가 큼. 유지보수 부담도 큼
현실적인 권장 순서
프롬프트+ControlNet → 텍스트 임베딩 → LoRA → (정말 필요하면) 풀 파인튜닝
선택 가이드 표
| 접근 | 무엇을 해결 | 한계 | 이런 경우에 |
| 프롬프트만 | 빠른 스타일 실험 | 레이아웃 붕괴 | 구조 중요치 않을 때 |
| ControlNet(Depth/Edge) | 레이아웃 보존, 스케일감 | 스타일 자유도 감소 | 가구 위치·크기 유지 필수 |
| + Segmentation | 카테고리별 재질 유지 | 세그맵 생성 비용 | 벽/바닥/가구를 구분해야 할 때 |
| 텍스트 임베딩 | 스타일 재현성 | 구조는 못 잡음 | “브랜드 톤” 고정 |
| LoRA | 스타일·재질 일관성 | 데이터 편향 | 특정 재질·톤 고정 |
| 풀 파인튜닝 | 전반적 성능 상향 | 비용/시간 큼 | 제품 상용화, 대규모 트래픽 |
웹 구현 구조 예시(파일 트리 포함)
/interior-gen/
├─ frontend/
│ ├─ src/
│ │ ├─ App.jsx
│ │ ├─ components/
│ │ │ ├─ ThreeRoomCanvas.jsx
│ │ │ ├─ StylePicker.jsx
│ │ │ └─ ResultGallery.jsx
│ │ ├─ libs/
│ │ │ └─ api.js
│ │ └─ styles.css
│ └─ public/
│ └─ index.html
└─ backend/
├─ main.py
├─ routers/
│ ├─ generate.py
│ ├─ preprocess.py
│ └─ health.py
├─ services/
│ ├─ controlnet_pipeline.py
│ ├─ embedding.py
│ └─ upscaler.py
├─ utils/
│ ├─ json_to_layoutmap.py
│ ├─ depth_estimator.py
│ └─ seed_tools.py
└─ models/
└─ lora/
└─ nordic_minimal_v1.safetensors
핵심 흐름
- 사용자가 three.js에서 가구 배치 후 scene.json 다운로드 + 동일 뷰로 capture.png 캡처.
- 프론트에서 두 파일 업로드(POST /generate).
- 백엔드가 전처리(json_to_layoutmap.py, depth_estimator.py)로 컨트롤 맵 생성.
- controlnet_pipeline.py가 Depth+Edge(+선택 Seg)로 1차 생성.
- upscaler.py가 업스케일 및 디테일 보정.
- 결과 URL을 프론트로 반환 → ResultGallery가 스타일별 썸네일 그리드로 표시.
JSON 스키마 예시와 전처리 팁
예시: scene.json
{
"room": { "width": 402.6, "depth": 429.1, "height": 230 },
"camera": { "fov": 55, "target": [200, 100, 200], "position": [450, 200, 450] },
"objects": [
{ "name": "Bed", "type": "bed", "pos": [100, 0, 105], "size": [150, 60, 200], "ry": 0 },
{ "name": "Desk", "type": "desk", "pos": [343, 0, 32], "size": [120, 75, 60], "ry": 0 }
]
}
전처리 핵심
- three.js 카메라 파라미터를 그대로 백엔드 깊이 추정/렌더에 반영.
- JSON을 평면도/세그맵으로 렌더할 때 픽셀 격자와 카메라 투영을 캡처와 맞추기.
- 바닥/벽/가구 클래스 인덱스를 고정해 세그멘테이션 맵 일관성 확보.
프롬프트 설계 체크리스트
- 첫 줄에 “Keep exact layout. Do not move or resize furniture.” 같은 구조 보존 문장.
- 스타일 문구는 명확한 재질·광원·렌즈로 구체화.
예: “soft daylight, f/5.6, 24–70mm, oak wood grain, matte wall paint” - 금지어도 명확히.
예: “no built-in wardrobes, no alcoves, no extra furniture” - 네거티브 프롬프트에 CG/plastic look, over-smoothing 포함.
하이퍼파라미터 운용 원칙
- Control strength를 0.6~0.9 구간에서 스윕. 레이아웃이 흔들리면 ↑, 텍스처가 메마르면 ↓.
- Guidance/CFG는 4~8 사이를 기본. 너무 높으면 과도한 텍스트 종속으로 왜곡.
- 시드 고정으로 같은 배치, 다른 스타일을 안정적으로 비교.
- 스타일 세트별로 프리셋 JSON을 만들어 재현성 확보.
일관성 전략
- 동일한 컨트롤맵과 시드를 유지한 채 스타일 임베딩/LoRA만 교체.
- 결과가 흔들리면 ① 업스케일러 단계 고정 ② 인페인팅으로 국소 보정.
- 가구 재질·색상까지 일관하게 하려면 소형 LoRA로 톤 고정이 효과적.
임베딩/LoRA/파인튜닝을 고려한 데이터 계획
- 텍스트 임베딩용: 스타일별 대표 이미지 30~50장. 톤·광원 일관성이 중요.
- LoRA용: 100~300장. 같은 가구 카테고리를 다양한 각도·조명으로.
- 풀 파인튜닝: 2천 장 이상 권장. 가능하면 레이아웃/세그라벨 포함.
비용과 일정이 크므로, 제품화 직전 “결정적 병목”일 때만 선택하는 게 현실적입니다.
품질 기준과 자동 평가
- 구조 정확도: 생성 이미지에서 객체 검출/분할 후, JSON 투영 위치와의 IoU 측정.
- 스타일 적합도: CLIP 유사도(텍스트 “nordic minimal” vs. 이미지).
- 재질 충실도: 재질 키워드(“oak”, “linen”)에 대한 이미지 패치 임베딩 유사도.
간단한 메트릭이라도 CI처럼 자동화하면 튜닝 속도가 급격히 올라갑니다.
비용·속도 최적화
- 전처리(깊이/에지/세그)는 캐시. 같은 캡처엔 다시 계산하지 않기.
- 스타일 프리셋별로 배치 생성하고, 업스케일은 합격작만.
- 로컬 실험은 저해상도에서 튜닝 완료 → 클라우드에서 고해상도 한 번에.
반응형