
ComfyUI × 동영상 생성 AI × Codex로 게임용 스프라이트 애니메이션을 양산하는 파이프라인을 구축한 이야기
요약
ComfyUI와 Codex를 활용하여 게임용 스프라이트 애니메이션을 대량 생산하는 자동화 파이프라인 구축 사례를 소개합니다. 캐릭터 시트 생성부터 LoRA 학습, 모션화, 도트 에셋 변환 및 정규화까지의 전 과정을 다룹니다.
핵심 포인트
- ComfyUI 노드 기반의 애니메이션 생성 워크플로우 구축
- Codex CLI를 커스텀 노드로 호출하여 비용 효율적 운영
- 프레임 일관성 유지를 위한 프롬프트 설계 및 실패 사례 공유
- AI 생성 이미지를 실제 1픽셀 단위 도트 에셋으로 정규화하는 방법
안녕하세요, Lise라고 합니다.
여러분은 게임 에셋을 AI로 만들어 보려고 시도한 적이 있으신가요?
X(Twitter)를 보고 있으면 수상쩍은 종량제 게임 에셋 생성 서비스들이 속속 올라오곤 하는데, 단발성 에셋이라면 몰라도 대량의 캐릭터 에셋이 필요한 경우에는 다소 비효율적이거나 적합하지 않다는 생각이 듭니다. 비용도 많이 들고요.
그래서 '그들이 할 수 있다면 나도 만들 수 있을 것'이라고 생각하여 직접 구축해 보았다는 회고록입니다.
결론부터 말씀드리면, **「캐릭터 시트 1세트 → LoRA 학습 → 포즈 생성 → 동영상 AI로 모션화 → 생성 AI로 도트 에셋 변환 → 투명화·정규화」**라는 완전한 파이프라인을 ComfyUI 상에서 구축하는 형태가 되었습니다.
캐릭터 1개당 7개 모션, 총 58개의 스프라이트를 인간의 작업은 「실행 버튼을 누른다」, 「좋은 프레임을 선택한다」만으로 납품 형식까지 가져갈 수 있습니다.
최종적인 타협과 계산도 있어 100% 만족스러운 품질의 결과물을 만들 수는 없었지만, 가능성을 느꼈기에 일단 정리하는 차원에서 기사로 써보려 합니다.


여기에 도달하기까지 수많은 함정을 밟았습니다. 본 기사는 워크플로우 소개를 주축으로 하되, 각 공정에서 「시도했다가 버린 방식」과 「왜 버렸는가」를 칼럼 형식으로 끼워 넣겠습니다. 오히려 그쪽이 본체일지도 모릅니다.
- ComfyUI로 「정지화상 생성 → 동영상 AI로 모션화 → 프레임 선별 → 도트 에셋 변환」을 일괄 처리하는 구성
- Codex CLI (ChatGPT 구독)를 ComfyUI의 커스텀 노드(Custom Node)로 호출하는 방법 - 종량제 결제를 피하고 싶다!
- 생성 AI로 애니메이션 프레임의 **일관성 (Consistency)**을 유지하기 위한 프롬프트 설계 (및 실패 패턴)
- 「AI가 그린 도트풍 이미지」를 진짜 1도트=1픽셀로 정규화하는 방법
- 비결정적이고 비용이 많이 드는 생성 공정을 안전하게 운용하기 위한 설계 원칙
본론에 들어가기 전에, ComfyUI가 무엇인지에 대해 말씀드리겠습니다.
ComfyUI는 Stable Diffusion 등의 이미지 생성 모델을 노드 그래프(Node Graph, 상자를 선으로 연결하는 형식)로 구동하는 워크플로우 엔진입니다. 「모델을 읽는다」, 「프롬프트를 인코딩한다」, 「샘플링한다」, 「저장한다」와 같은 처리가 전부 노드로 분해되어 있어, 이를 자유롭게 다시 연결할 수 있습니다.
이미지 생성 로컬 환경으로는 Stable Diffusion WebUI (AUTOMATIC1111)가 더 유명할 것이므로, 그 차이점을 들어 설명하겠습니다.
| 구분 | Stable Diffusion WebUI | ComfyUI |
|---|---|---|
| 조작 | 폼에 입력하여 생성 버튼 클릭 | 노드를 연결하여 그래프 생성 |
| 특징 | 워크플로우 = JSON 파일 (저장·공유·스크립트 생성 가능) | |
| 확장 | 확장 기능 (Extension) | 커스텀 노드 (Python 클래스 하나로 직접 제작 가능) |
WebUI가 「고기능 생성 폼」이라면, ComfyUI는 「이미지 처리 비주얼 프로그래밍」입니다.
단일 이미지를 시행착오하며 만드는 것이라면 WebUI가 간편하겠지만, 본 기사와 같은
- ControlNet으로 스탠딩 일러스트 생성 → 동영상 AI로 모션화 → 프레임 선별 → 도트 에셋 변환 → 투명화·정규화
라는 다단계 파이프라인을 구축하여 반복적으로 돌리는 용도에서는 ComfyUI가 유일한 선택지입니다.
- 중간에 인간의 선별 과정을 끼워 넣을 수 있음 (실행을 일시 정지하고 이미지를 선택하게 하는 노드가 있음)
- 변경되지 않은 노드는 재실행되지 않음 (캐시가 적용되므로 후반부만 몇 번이고 조정 가능)
- 워크플로우가 단순한 JSON이므로, 스크립트로 생성 및 양산 가능 (후술할 양산 아키텍처는 이것을 전제로 함)
또한, 동영상 생성 (Wan2.2 등)이나 직접 만든 Python 노드와 같은 「Stable Diffusion 이외의 것」도 동일한 그래프에 섞을 수 있다는 점이 편리합니다.
| 역할 | 사용 기술 |
|---|---|
| 워크플로우 엔진 | ComfyUI |
| ... |
[캐릭터 준비 (1회만)]
캐릭터 시트 생성 → 화풍 통일 → LoRA 학습 → 포즈형·도트 기준 이미지 준비
[모션별 워크플로우]
...
Attack / Dead와 같이 「결정적 포즈(決めポーズ)가 존재하지 않는 모션」은 Step 1/2를 생략하고, Idle의 스탠딩 일러스트에 여백을 준 것을 시점으로 I2V(Image-to-Video)부터 시작합니다 (후술).
파이프라인의 전제로, 캐릭터의 동일성은 프롬프트가 아니라 LoRA로 담보하고 있습니다. ChatGPT 등을 사용하여 다각도 캐릭터 시트를 만들고, 베이스 모델의 화풍에 img2img로 맞춘 뒤 kohya_ss로 학습시키는——라는 왕도적인 흐름입니다 (본 기사의 주제가 아니므로 상세 내용은 생략).
IP-Adapter라는 선택지도 있었지만 최종적으로는 사용하지 않았습니다.
처음에는 IP-Adapter로 즉석 전사를 시도했으나, 사용 모델(v-pred 계열 SDXL)과 상성이 좋지 않아 붕괴되었습니다. 애초에 IP-Adapter는 「느슨하게 맞추는」 용도이며, 게임 에셋에 요구되는 엄격한 동일성에는 LoRA가 더 적합했습니다. 「몇 장의 good한 이미지를 만든다 → LoRA화 → 양산」하는 방식이 결국 안정적입니다.
Idle / Walk는 「정답 포즈」가 있으므로, 기존 스프라이트(또는 직접 만든 포즈 이미지)를 **depth ControlNet의 틀(型)**로 사용하고, LoRA를 적용한 txt2img로 후보를 여러 장 생성합니다.

포인트:
- depth는 포즈뿐만 아니라 무기의 실루엣도 고정하기 때문에, 무기가 다른 캐릭터에 틀을 유용하면 무기까지 고정됩니다. 캐릭터를 생성할 때는 무기마다 틀을 가질 필요가 있습니다.
- 치비 캐릭터(2등신)에는 openpose를 사용할 수 없습니다. 골격 검출이 깨지기 때문에 아마도 depth가 유일한 선택지일 것입니다.
- 생성 후보 중 인간이 한 장을 선택하는 공정은, ComfyUI의
Image Filter계열 노드(실행을 멈추고 팝업으로 선택)를 통해 「실행 → 클릭으로 선택」하는 동작만으로 구성할 수 있습니다. - 조금 선택하기 번거롭지만 가장 편합니다.
공격 포즈는 ControlNet으로 만들 수 없다 (순환 문제)
공격의 「휘두르기 전 준비(振りかぶり)」 포즈 틀을 만들려고 하면 깨닫게 됩니다——준비 동작의 틀을 만들려면 준비 동작의 그림이 필요하다. 닭이 먼저냐 달걀이 먼저냐의 문제입니다. 결론적으로, 새로운 포즈는 ControlNet이 아니라 프롬프트 구동 방식의 txt2img로 「캐스팅」(seed 가차로 좋은 한 장을 고르는 것)하거나, 다음 절의 동영상 AI에 통째로 맡기는 것이 정답이었습니다.
게임 에셋을 만들려고 시도해 본 적이 있는 사람이라면 잘 알겠지만,
이미지 생성 AI는 좌우 다리의 인식이 어려워, 중간 프레임(중간 단계 동작)을 안정적으로 만들 수 없습니다.
이 부분이 이 파이프라인의 흥미로운 지점인데, 모션의 중간 프레임을 인간이 그리지 않고, 이미지 → 동영상 AI(Wan2.2 I2V)에 맡깁니다.

채택한 스탠딩 일러스트 1장을 시점으로 「She starts to walk / performs an attack combo...」라는 프롬프트로 49 프레임의 동영상을 생성하고, 그중에서 좋은 프레임을 선별합니다. 걷기 동작의 다리 교차 운동도, 공격의 준비 동작 → 휘두르기 동작도 동영상 모델이 알아서 만들어 줍니다.
실운용의 요점:
- start-only(종점 없음)가 기본입니다. 시작점과 종점을 모두 고정하는 방식(FLF)은 「종점의 품질이 모든 것을 지배한다」는 성질이 있어, 종점에 다리가 그려져 있지 않으면 모든 프레임에서 다리가 사라지는 등의 사고가 발생합니다.
- 공격처럼 시점의 범위를 벗어나는 움직임이 있는 모션은, 시작점 이미지에 여백을 둡니다 (캐릭터를 축소하여 아래쪽으로 배치하고, 휘두를 무기의 헤드룸을 확보).
- 생성은 run 단위로 폴더에 축적하며 (
run_00000.webp+ 모든 프레임 PNG), 인간은 webp를 보고 채택할 run을 결정한 뒤 프레임을 선택하는 2단계 선별 방식을 사용합니다. - 배면(뒷모습) 모션은 「뒤돌아보기」와의 싸움입니다. 방향의 고정은 negative 프롬프트로 (
(turning around:1.6), (front view:1.6)...), 움직임의 형태는 positive로 유도합니다. 동작을 negative에 넣으면 움직임 자체가 죽어버립니다.
「보행은 키프레임이 필요 없다」지만 「루프에는 사용할 수 없다」
Walk용 포즈 틀을 만들지 않고 「Idle 스탠딩 일러스트에서 직접 걷게 하기」 실험도 했습니다. 보행 자체는 훌륭하게 생성되지만, 도입부가 반드시 「idle → 걷기 시작」의 전이가 되기 때문에, 루프를 전제로 하는 Walk 애니메이션에는 사용하기 어렵습니다. 루프시키고 싶은 모션은 미리 루프의 일부 이미지를 먼저 생성하는 편이 안정적입니다.
당초에는 도트 그래픽(Pixel Art)이 아니라 일반적인 일러스트 애니메이션 에셋을 계획하고 있었습니다만,
LoRA를 통한 세밀한 디자인 보장이 어려워, 20장이나 30장을 생성해야 겨우 일관성을 유지한 이미지가 1장 나올까 말까 한 '이미지 가챠(Gacha)' 상태가 되어 결국 방침을 변경할 수밖에 없었습니다.
세밀한 의상 디자인이 프레임마다 달라지면 보는 사람에 따라 위화감을 느낄 수 있기 때문입니다.
저는 이번에 도트 그래픽으로 전환함으로써 이를 회피했지만, 게임 화면상의 캐릭터 에셋 표시를 작게 만드는 것, 즉 정보량을 줄이는 방법으로도 회피할 수 있을 것이며, 로컬 머신(Local Machine)의 자원이 남는다면 가챠를 계속 돌리는 방법도 하나의 선택지라고 생각합니다.
이번에는 제가 선택한 "도트 그래픽 변환으로 우회하기" 형태의 대응 방식을 기술하겠습니다.
선별한 프레임(일러스트풍)을 도트 그래픽으로 변환합니다. 이 부분이 가장 시행착오가 많았던 공정입니다.
처음에 누구나 시도하는 방식은 "축소 + 색상 감소"이지만, 결과는 그저 **"축소만 된 이미지"**일 뿐입니다. 도트 그래픽의 본질(의도를 가진 윤곽선, 단색으로 채워진 면, 클러스터 단위의 음영)은 해상도가 아니라 그리는 방식이기 때문에 기계적인 처리로는 만들어지지 않습니다. SDXL 계열의 픽셀 아트(Pixel Art) LoRA도 시도해 보았으나, 품질이 실용적인 수준에 도달하지 못했습니다.
결정적인 계기가 된 것은 Codex(ChatGPT의 코딩 에이전트)에 내장된 이미지 생성 도구였습니다. 단독 테스트를 통해 "진정한 픽셀 클러스터·윤곽선·캐릭터 동일성 유지"를 확인할 수 있었기에, 로컬의 Codex CLI를 자식 프로세스(Child Process)로 호출하는 커스텀 노드를 제작했습니다.
# 컨셉(발췌): codex exec를 비대화형(Non-interactive) 모드로 호출하여, 이미지를 첨부하고 결과 PNG를 회수함
cmd = [codex, "exec",
"--skip-git-repo-check", "--ephemeral",
...
주의할 점:
codex exec는 stdin이 파이프(Pipe)일 경우 입력을 기다리며 행(Hang) 상태에 빠집니다. 프롬프트를-를 통해 stdin으로부터 전달하고 즉시 종료하는 방식으로 회피했습니다.- API 키는 필요 없습니다 (ChatGPT 구독 로그인을 그대로 사용). 단, 플랜의 레이트 리밋(Rate Limit)을 소비합니다. 대량으로 상시 사용하는 용도가 아니라, 어디까지나 개인 개발 규모의 수법입니다.
- 시드(Seed)가 없어 매번 결과가 달라집니다. 이러한 비결정성(Non-determinism) 때문에 후술할 운용 설계가 필요합니다.
프레임을 한 장씩 독립적으로 변환하면, 프레임 간에 디자인이 흔들립니다 (무기의 문양, 옷의 패턴, 소품의 유무가 프레임마다 달라짐). 대책은 2단계로 구성됩니다.
① 레퍼런스(Reference) 방식: 매 프레임 변환 시, 승인된 도트 그래픽 1장을 "Image A(기준)"로서 함께 첨부하고, "Image B(대상 프레임)를 Image A의 스프라이트 세트의 일원으로 변환하라"고 지시합니다. 기준은 모션 자세별로 가집니다 (서 있는 포즈를 기준으로 공격 프레임을 변환하면 정밀도가 떨어집니다).
② 포즈와 디자인의 역할 분리 프롬프트: 이것이 가장 중요한 배움입니다. 당초 "헷갈리면 Image A의 외형을 복사하라"는 강한 추종 지시를 넣었더니, 디자인은 안정되는 대신 포즈까지 A로부터 복사되는 사고가 발생했습니다 (걷기 애니메이션에 직립 포즈가 섞여 들어감). 최종 형태는 다음과 같이 "무엇을 A에 따르게 하고, 무엇을 B에 따르게 할 것인가"를 명시적으로 분리하는 방식입니다:
STRICT ROLE SPLIT (highest priority):
- Image B is the ONLY source of the POSE: body posture, limb positions,
leg stride, weapon angle. NEVER borrow the pose from Image A.
...
옵티컬 플로우(Optical Flow) 방식의 활용 구분
일관성 대책으로서 "Frame 0만 도트 그래픽화하고, 나머지 프레임은 원본 영상의 옵티컬 플로우(RAFT)를 이용해 도트 그래픽의 픽셀을 물리적으로 워프(Warp)시키는" 방식도 구현했습니다.
픽셀이 말 그대로 운반되기 때문에 원리적으로 그림체가 흔들리지 않으며, Idle(미세한 움직임)에서는 훌륭하게 작동하여 본 채택되었습니다.
하지만 Attack이나 Walk에서는 Idle과 달리 캐릭터의 자세나 신체 부위의 위치가 크게 변하기 때문에, 이 방법을 사용하면 Idle의 포즈에 끌려가 Attack이나 Walk의 올바른 시선이 반영되지 않았습니다. (당연한 결과라고 할 수 있습니다.)
단순히 「흔들리는」 애니메이션이라면 옵티컬 플로우 (Optical Flow) 방식을 사용하고, 그렇지 않다면 솔직하게 원본 이미지에서 도트 에디트 (Pixel Art) 변환을 하는 편이 좋을 것입니다.
ChatGPT뿐만 아니라, 생성 AI에게 「사이즈를 바꾸지 마라・위치를 지켜라」라고 아무리 지시해도, ±수%의 사이즈 흔들림이나 심할 때는 캔버스 비율의 20%를 초과하는 위치 어긋남이 발생합니다. 프롬프트(Prompt)를 통한 억제에는 한계가 있으므로, 변환 후에 원본 프레임과의 bbox(Bounding Box) 비교를 통한 기계적 보정을 수행합니다.
사이즈: 캐릭터 bbox의 높이 비율만으로 스케일 보정. 너비는 머리카락의 흔들림이나 무기의 각도에 따라 정당하게 변동하기 때문에, 너비를 포함하면 「너비 +8%・높이 -8%」가 평균 0%로 상쇄되어 보정이 스킵되는 함정이 있습니다 (실제로 겪었습니다).
위치: bbox 중심을 원본 프레임의 bbox 중심으로 평행 이동. 원본 영상은 진정한 모션 궤적을 가지고 있으므로, 위치의 정답은 항상 소스(Source) 측에 있습니다. 발밑 앵커(Anchor)와 같은 자세 가정은 공중 포즈에서 파괴되므로, bbox 중심이 안전합니다.
Codex의 출력은 「도트 에디트풍의 고해상도 이미지」입니다 (1도트가 4~7픽셀의 클러스터로 그려져 있음). 게임 에셋에는 진정한 1도트 = 1픽셀이 필요하므로, 후처리로 정규화합니다.
「몇 % 축소해야 1:1이 되는가」는 생성할 때마다 달라집니다 (Codex의 출력 캔버스 사이즈 자체가 가변적이며, 도트 피치(Dot Pitch)도 2.7px~5.8px로 흔들림). 고정된 값을 사용하는 것은 불가능하므로, 이미지로부터 피치를 자동 추정합니다.
- 에지 강도 프로파일 (Edge Strength Profile, 인접 픽셀 차분의 행/열 평균)을 추출
- 그 **자기 상관 (Autocorrelation)**의 피크(Peak)로부터 그리드 주기를 추정 (포물선 보간을 통한 서브픽셀 정밀도 적용)
- 경계 위치의 에지 가중 히스토그램으로 위상 (Phase) (그리드의 시작 오프셋)을 추정
- 각 셀의 중심을 nearest sampling → 진정한 1:1 도트 이미지
빠지기 쉬운 함정이 두 가지 있습니다:
배음(Harmonics)으로의 오인식: 자기 상관은 종종 기본 주기의 2배에서도 피크를 가지며, 노이즈에 따라 그쪽이 미세하게 우세할 수 있습니다. X축은 피치 2.8, Y축은 5.0으로 고정되어 세로만 찌그러진 이미지가 나온 적이 있습니다. 대책은 「글로벌 최대값」이 아니라 「최대값의 85% 이상인 국소 피크 중 최소 래그(Lag)」를 채택하여 기본 주기를 우선시하는 선택과, X/Y 피치의 강제 통일 (도트 에디트의 전제로 정사각형 도트 사용)입니다.
탐색 하한선: 캔버스가 작으면 피치가 탐색 범위의 하한선을 밑돌아, 기본 주기가 후보에서 사라지고 배음만 남게 됩니다. 하한선은 2.0까지 열어둡니다.
도트 에디트의 투명화에 AI 세그멘테이션 (Segmentation, RMBG 계열)은 적합하지 않았습니다. 흰색 배경과 같은 색의 무기 장식까지 「배경」으로 오해하여 투명화해 버립니다. 채택한 방법은 고전적인 컬러 키 (Color Key) 추출 + 두 가지의 기교입니다:
- 경계 연결 판정: 배경색에 가까운 픽셀 중 「이미지의 가장자리에 연결된 영역」만을 배경으로 간주. 캐릭터 내부의 흰색 (머리 장식, 하이라이트)이 보호됩니다.
- 배경 마스크의 1px 팽창: 생성 이미지의 에지는 약간 소프트하여, 임계값(Threshold)에 걸리지 않는 중간색이 외곽 1px에 남습니다. 축소해도 흰색 반점으로 잔존하기 때문에, 연결 판정 이후에 1px 팽창시켜 제거합니다 (순서가 중요합니다. 먼저 팽창시키면 내부의 흰색까지 포함하게 됩니다).
납품용 스프라이트는 각 프레임을 **자신의 알파 최소 영역 (Alpha Bounding Box)**으로 잘라냅니다. 프레임 간의 상대 위치는 상실되지만 (정렬은 Unity 측의 피벗(Pivot)에서 수행), 결정적인 이점이 있습니다: 쓰레기 도트가 하나라도 남아 있으면 캔버스가 부자연스럽게 커지며, 에셋 단계에서 한눈에 발각된다는 점입니다. 품질 보증이 전 공정으로 이동하는 것입니다.
솔직히 말하자면, 「위치와 사이즈를 완전히 맞춘 에셋」은 포기했습니다. 이 부분은 본 파이프라인의 가장 큰 타협점이기 때문에, 항목으로 분리하여 정리합니다.


「어긋남」에는 세 가지 레벨이 있으며, 각각 다루는 방식이 다릅니다.
| 레벨 | 무엇이 어긋나는가 | 대응 |
|---|---|---|
| ① 프레임 내 | 생성 AI가 원본 프레임과 다른 사이즈/위치로 그림 | 기계적 보정으로 해결 (앞서 언급한 bbox 보정) |
| ② 프레임 간 | 애니메이션 재생 시 각 코마(Frame)의 상대 위치 | 에셋에 포함하지 않음 (최소 영역 crop 및 Unity 피벗으로 정렬) |
| ③ 모션 간 | Idle과 Walk에서 캐릭터의 도트 신장이 다름 | 일단 타협 |
전절에서 언급한 대로, 각 프레임은 자신의 알파 최소 영역으로 잘라내기 때문에, 「캐릭터가 화면 어디에 서 있었는가」라는 정렬 정보는 에셋에서 사라집니다.
이것은 최종적으로 Unity 에셋으로서 SpriteAtlas가 되기 때문에 이 정보는 불필요하며,
「점프해서 공격하기」와 같은 애니메이션의 경우에는 이미지 인식으로 기준 위치를 특정할 수 없기 때문에, 어차피 사람이 조정해야 합니다.
한편, 이 부분은 순수한 타협입니다. Codex의 출력은 캔버스 크기와 도트 피치(dot pitch)가 실행할 때마다 흔들리기 때문에, 1:1 정규화 후의 도트 신장(dot height)이 모션마다 달라집니다. 실측 결과 대부분의 모션은 신장 190~200도트 클러스터에 수렴했으나, +12~19% 더 큰 모션과 -12% 더 작은 모션이 남았습니다. 이대로 애니메이션을 전환하면 캐릭터의 겉보기 크기가 모션 경계에서 튀게 됩니다.
이를 맞출 수단을 검토해 보았지만, 모두 효율이 좋지 않아 일단 타협했습니다.
게임 상에서 도저히 눈에 띈다면 그때 다시 생각해보려 합니다.
파이프라인에서 기준 신장으로 확대/축소한다 → 1:1 도트 이미지를 비정수 배율로 스케일링하면 도트 격자(dot grid)가 깨집니다 (1도트=1픽셀을 지키려면 정수 배율만 허용되며, ±12%와 같은 어중간한 보정은 원리적으로 불가능)
이상치(outlier) 모션을 재생성한다 → Codex는 비결정적(non-deterministic)이므로 원하는 신장이 나올 때까지 가챠(gacha)를 돌려야 하며, 레이트 제한(rate limit) 측면이나 품질 측면에서 수지가 맞지 않음
Codex에는 시드(seed)가 없어 실행할 때마다 결과가 달라집니다. 게다가 ComfyUI의 실행 캐시는 서버 재부팅 시 삭제됩니다. 이 조합 때문에 「좋은 결과가 나왔는데, 노드를 수정하고 재부팅 후 재실행했더니 모든 프레임이 다시 추첨되어 양품을 잃어버리는」 사고를 실제로 겪었습니다. 이에 대한 대책으로 두 가지 설계 원칙을 세웠습니다.
① 생성과 후처리를 동일 워크플로우에 공존시키지 않는다
「비싸고 비결정적인 공정 (Codex 생성)」과 「무료로 몇 번이고 다시 하고 싶은 공정 (투명화·정규화·크롭)」을 별도의 워크플로우로 분리합니다. 생성물은 반드시 디스크에 저장하고, 후처리는 그것을 읽어옵니다. 후처리 파라미터를 아무리 조정해도 재추첨은 일어나지 않습니다.
② 프레임 단위의 영구 디스크 캐시
생성 노드 자체에도 (참조 이미지 + 대상 이미지 + 프롬프트)의 해시 → 결과 PNG 형태의 영구 캐시를 갖게 했습니다. 효능은 세 가지입니다:
중간 실패로부터의 재개: 레이트 제한으로 8프레임 중 4프레임째에 끊겨도, 재실행 시에는 이미 성공한 3프레임을 캐시에서 반환하고 남은 프레임만 생성합니다 (소비된 쿼터가 낭비되지 않음)
재부팅 내성: ComfyUI를 몇 번을 재부팅해도 동일 입력은 동일 결과
반대로 의도적으로 재추첨하고 싶을 때는 캐시를 삭제한다 (또는 프롬프트를 미세 수정한다)라는 명시적인 조작이 됩니다.
워크플로우가 완성되어도 캐릭터마다 모든 워크플로우를 복제하고 수정하는 방식으로는 파산하게 됩니다. 최종 형태는 「캐릭터 고유 정보」와 「공통 처리」의 분리입니다.
chars/alice.json ← 캐릭터 프로필 (고유 정보의 원본)
├ lora: 캐릭터 LoRA 파일명
├ sd_identity: 정지화상용 태그 단편 ("trigger word, 1girl, solo, holding scythe")
...
공통 부품은 서브그래프 (Blueprint)로 정의하여 모델, 프롬프트 정형 문구, cfg 등의 「문맥 없는 파라미터」를 구워 넣습니다. 캐릭터 고유 정보 (LoRA가 적용된 모델, 프롬프트 단편, 참조 이미지)는 입력 소켓을 통해 외부에서 받습니다.
프롬프트는 StringConcatenate를 사용하여 「캐릭터 단편 (입력) + 모션 정형 문구 (구워 넣은 것)」를 합성합니다. 정형 문구에는 she/her/cape와 같은 성별·의상 관련 단어를 쓰지 않습니다 (캐릭터 비의존성을 스크립트로 검사하여 기계적으로 체크함). 서브그래프 정의는 Python 스크립트를 원본으로 하며, 워크플로우 JSON과 라이브러리 등록 파일도 모두 스크립트에서 생성합니다. ComfyUI의 Blueprint는 user/default/subgraphs/*.json에 위치할 뿐이므로 직접 생성할 수 있습니다.
폴더 규약도 양산을 전제로 정리했습니다.
input/Chara/{캐릭터}/Common/ = 사람이 준비한 소재 (포즈형·도트 기준·키프레임)
output/Chara/{캐릭터}/{모션+방향}/Step{N}/
= 파이프라인 생성물. 각 워크플로우는 본인 명의의 폴더에만 기록한다 (읽기는 자유) —— 생산자는 소비자를 모르는 일방통행적 의존 - 최종 Step의 직하단 = 납품물만 존재. 확인용 (raw/투명 풀/애니메이션 webp)은 서브 폴더로
코어의 LoadImage는 서브 폴더를 읽을 수 없다
ComfyUI 코어의 LoadImage는 input 직하단만 열거 및 수용합니다 (combo 검증에서 차단됨). 계층 폴더 운용에는 "경로 문자열로 읽는" 자체 제작 로더가 필요합니다. 덧붙여 root (input/output)를 선택식으로 만들면, "소재=input / 결과물=output"이라는 본래의 의미론에 부합하는 설계를 할 수 있습니다.
이 개발을 통해 확립한 원칙을 나열합니다. 생성 AI (Generative AI) 파이프라인 전반에 응용할 수 있을 것입니다.
동일성은 LoRA, 구조는 ControlNet, 변화는 프롬프트, 일관성은 레퍼런스 — 각 수단의 특기 분야를 섞지 말 것 -
포즈의 정답과 디자인의 정답을 분리하여 지시한다 — 참조 이미지에 대한 추종을 강화할수록 포즈 오염(Pose Contamination)의 리스크가 높아짐 -
생성 AI의 출력은 기계적 보정으로 마무리한다 — 크기·위치는 프롬프트에 의존하지 말고 bbox (Bounding Box)로 수정할 것 -
수치 검증은 필요조건, 최종 판정은 육안 — 평균 오차는 회전 파괴나 검은 영역의 포즈 오류를 잡아내지 못함 -
비결정적이고 비용이 많이 드는 공정과, 결정적이고 반복하고 싶은 공정을 분리한다 — 양품은 디스크에 저장하여 자산화할 것 -
쓰기 소유권 — 각 공정은 본인 명의의 폴더에만 기록한다. 디버깅과 정리가 한꺼번에 쉬워짐 -
품질 보증은 전 공정으로 — 최소 프레임 크롭(Crop)처럼 "불량을 눈으로 알 수 있는" 형식을 납품물로 선택할 것 -
원본은 스크립트, UI는 일회용 — 워크플로우 JSON도 Blueprint도 생성물이다. 수동 수정은 하지 않는다
게임 캐릭터 에셋을 생성할 때 가장 먼저 막히는 부분이 걷기 애니메이션이라고 생각했기에, 동영상 생성 AI를 경유하여 포즈를 담보한다는 아이디어가 실제로 기능해서 다행이라고 생각합니다.
AI로 생성하는 이상 아무래도 인간이 눈으로 품질을 확인하는 절차가 필요했는데, 그것도 ComfyUI 상에서 구현할 수 있어서 좋았습니다.
최종적으로는 도트 에셋(Pixel Art) 변환으로 타협했지만, 이것도 이것대로 배울 점이 많아 해보길 잘했다고 생각합니다.
남은 과제는, 타협점 항목에 적어둔 Pixels Per Unit 보정이 실제 기기에서 어디까지 통용될지에 대한 검증과, 두 번째 캐릭터에서의 재현성 검증입니다. 마음이 내키면 다시 글로 작성하겠습니다.

AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기