블로그 목록으로
Engineering14분 읽기 

Perfect Pixel 엔진 딥다이브 — 문장 하나가 100개의 모션 스프라이트가 되기까지

“파란 갑옷의 기사”라는 한 문장이 8방향 스프라이트와 100개 이상의 모션으로 완성되는 과정. 생성 파이프라인, 셀프 교정 품질 루프, 리얼 픽셀라이즈까지 — Perfect Pixel 엔진의 내부를 처음으로 공개합니다.

작성NEXUS AI Labs

Perfect Pixel 엔진 딥다이브 — 문장 하나가 100개의 모션 스프라이트가 되기까지 대표 이미지

게임 프로토타입에서 가장 먼저 발목을 잡는 건 코드가 아니라 아트 에셋입니다. 캐릭터 하나가 걷고, 달리고, 공격하고, 피격당하는 애니메이션을 8방향으로 갖추려면 수백 장의 프레임이 필요합니다. 숙련된 픽셀 아티스트가 캐릭터 하나에 붙어 몇 주를 작업해야 하는 분량이죠. Perfect Pixel은 이 문제를 정면으로 겨냥합니다 — 텍스트 한 문장을 입력하면, 게임 엔진에 바로 임포트할 수 있는 8방향 · 100+ 모션 스프라이트 세트가 나오는 데스크톱 스튜디오입니다.

이 글에서는 Perfect Pixel의 심장인 생성 엔진을 해부합니다. 왜 범용 이미지 생성 모델을 그대로 쓰면 픽셀아트가 망가지는지, 그래서 우리가 어떤 파이프라인을 설계했는지, 그리고 셀프 교정 품질 루프리얼 픽셀라이즈라는 두 개의 핵심 장치가 결과물을 어떻게 프로덕션 품질로 끌어올리는지를 순서대로 다룹니다.

왜 “그냥 이미지 생성”으로는 안 되는가

최신 이미지 생성 모델에게 “pixel art knight”를 요청하면 그럴듯한 그림이 나옵니다. 하지만 그 결과물을 게임에 넣어보면 곧바로 세 가지 문제가 드러납니다.

  1. 가짜 픽셀 그리드 — 모델이 그리는 것은 ‘픽셀아트처럼 보이는 그림’입니다. 픽셀 하나의 크기가 위치마다 다르고, 격자에 정렬되어 있지 않으며, 경계에는 안티앨리어싱이 뭉개져 있습니다. 확대하는 순간 정체가 드러납니다.
  2. 팔레트 폭발 — 진짜 픽셀아트는 16~64색의 통제된 팔레트로 그려집니다. 생성 모델의 출력은 수만 가지 색을 씁니다. 프레임마다 색이 미묘하게 달라져 애니메이션으로 이으면 캐릭터가 ‘일렁’입니다.
  3. 프레임 간 일관성 붕괴 — 걷기 6프레임을 각각 생성하면 매 프레임 갑옷 디테일과 비율이 달라집니다. 애니메이션의 생명은 일관성인데, 독립 생성으로는 이것이 원천적으로 확보되지 않습니다.

결론은 명확했습니다. 생성 모델은 파이프라인의 한 단계일 뿐, 파이프라인 그 자체가 될 수 없다는 것. Perfect Pixel 엔진은 생성 모델의 앞뒤에 프롬프트 컴파일러, 품질 판정 루프, 결정론적 후처리를 배치해 이 세 문제를 각각 격파합니다.

아키텍처 — Go 코어와 React 스튜디오

Perfect Pixel은 Go 1.25 + Wails v2 기반의 macOS 데스크톱 앱입니다. 프론트엔드는 React 18 + TypeScript + Vite, UI는 Radix UI와 Tailwind 위에 HSL 커스텀 프로퍼티 토큰 시스템으로 구성했습니다. 무거운 작업 — 프로바이더 API 오케스트레이션, 이미지 디코딩, 픽셀 후처리, 스프라이트 시트 패킹 — 은 전부 Go 코어가 담당하고, React는 순수하게 스튜디오 UI에 집중합니다.

100+
캐릭터당 자동 생성 모션
8
방향 스프라이트 세트
4
AI 프로바이더 백엔드
1
필요한 입력 — 문장 하나
Perfect Pixel 스튜디오 메인 화면
Perfect Pixel 스튜디오 — 좌측 프로젝트 패널, 중앙 캔버스, 우측 생성 컨트롤로 구성된 메인 워크스페이스

Wails를 선택한 이유는 단순합니다. Electron 대비 바이너리가 10분의 1 수준으로 가볍고, Go의 고루틴이 모션 배치 생성 같은 대량 병렬 I/O에 이상적이기 때문입니다. 100개 모션을 생성할 때 엔진은 프로바이더의 rate limit에 맞춰 워커 풀을 조절하며 동시에 수십 개의 생성 요청을 흘려보냅니다. 이것을 Node 런타임에서 했다면 백프레셔 관리부터 다시 만들어야 했을 겁니다.

생성 파이프라인 — 문장에서 스프라이트 세트까지 6단계

사용자가 “파란 갑옷의 기사”라고 입력하면, 엔진 내부에서는 다음 여섯 단계가 순차·병렬로 실행됩니다.

  1. 프롬프트 컴파일 — 한 문장을 캐릭터 시트 명세로 확장합니다. 체형 비율, 팔레트 규모, 아웃라인 스타일, 조명 방향 같은 픽셀아트 규약을 시스템 프롬프트 레이어로 주입해, 어떤 프로바이더로 보내도 동일한 스타일 계약이 유지되게 합니다.
  2. 베이스 캐릭터 생성 — 컴파일된 프롬프트로 정면 기준 캐릭터를 생성합니다. 이 단계의 산출물이 이후 모든 방향·모션의 시각적 앵커가 됩니다.
  3. 8방향 파생 — 베이스 캐릭터를 레퍼런스로 남은 7개 방향을 생성합니다. 매 요청에 베이스 이미지를 함께 전달해 프레임 간 일관성 문제를 구조적으로 차단합니다.
  4. 모션 확장 — 걷기 · 달리기 · 공격 · 피격 · 사망 등 카테고리별 모션 템플릿에 따라 100개 이상의 애니메이션 프레임을 병렬 생성합니다. Go 워커 풀이 프로바이더별 rate limit에 맞춰 동시성을 조절합니다.
  5. 품질 루프 — 생성된 모든 프레임이 셀프 교정 루프를 통과합니다. 기준 미달 프레임은 실패 사유가 프롬프트에 반영되어 재생성됩니다 (다음 섹션에서 상술).
  6. 리얼 픽셀라이즈 & 내보내기 — 그리드 정렬, 팔레트 양자화, 시트 패킹을 거쳐 게임 엔진 친화 포맷으로 저장합니다.
Perfect Pixel 씬 편집 화면 — 생성된 캐릭터와 모션 프레임
생성된 캐릭터가 씬 위에서 모션 프레임 단위로 검수되는 모습

셀프 교정 품질 루프 — 생성을 믿지 않는 엔진

100개 모션을 병렬 생성하면 반드시 일부는 실패합니다. 손가락이 뭉개지거나, 방향이 뒤집히거나, 팔레트를 벗어난 프레임이 섞여 나옵니다. 초기 버전에서는 이것을 사람이 눈으로 골라냈는데, 100개 중 15개꼴로 재작업이 필요했습니다. 그래서 엔진에 검수자 역할의 AI를 하나 더 넣었습니다.

품질 루프는 생성 직후 각 프레임을 비전 모델에게 보내 구조화된 판정을 받습니다. 판정 기준은 실루엣 무결성, 방향 정확성, 팔레트 준수, 모션 카테고리 부합 — 네 축입니다. 판정이 불합격이면 실패 사유가 다음 생성 프롬프트에 교정 지시로 삽입되어 재생성됩니다. 같은 프레임이 3회 연속 불합격하면 루프를 끊고 사용자 검수 큐로 보냅니다. 무한 루프 방지와 비용 통제를 위한 회로 차단기입니다.

engine/quality_loop.go (개념 구조)go
// 프레임 하나의 품질 루프 — 판정 실패 사유를 프롬프트에 되먹임
func (e *Engine) generateWithQualityLoop(ctx context.Context, spec FrameSpec) (*Frame, error) {
    prompt := e.compiler.Compile(spec)
    for attempt := 1; attempt <= maxAttempts; attempt++ {
        frame, err := e.provider.Generate(ctx, prompt, spec.BaseAnchor)
        if err != nil {
            return nil, err
        }
        verdict := e.inspector.Judge(ctx, frame, spec) // 비전 모델 판정
        if verdict.Pass {
            return frame, nil
        }
        // 실패 사유를 교정 지시로 주입해 재생성
        prompt = prompt.WithCorrection(verdict.Reasons)
    }
    return nil, ErrNeedsHumanReview // 회로 차단 — 사용자 검수 큐로
}

이 루프를 넣은 뒤 사람 재작업 비율은 15%에서 2% 아래로 떨어졌습니다. 흥미로운 부수 효과도 있었습니다 — 실패 사유 로그가 쌓이면서 프롬프트 컴파일러의 약점이 데이터로 드러났고, 시스템 프롬프트를 개선하는 근거가 됐습니다. 엔진이 스스로의 교사 데이터를 만드는 셈입니다.

리얼 픽셀라이즈 — ‘픽셀아트처럼 보이는 그림’을 진짜 픽셀아트로

품질 루프를 통과한 프레임도 아직은 ‘픽셀아트처럼 보이는 고해상도 이미지’입니다. 리얼 픽셀라이즈는 이것을 결정론적 후처리로 진짜 픽셀아트로 변환하는 단계입니다. AI가 아니라 순수 알고리즘이라는 점이 중요합니다 — 같은 입력이면 언제나 같은 출력이 나와야 게임 에셋으로 신뢰할 수 있기 때문입니다.

  • 그리드 감지 & 스냅 — 이미지에서 지배적인 ‘의도된 픽셀 크기’를 주파수 분석으로 추정하고, 모든 픽셀을 균일 격자에 재정렬합니다. 가짜 픽셀 그리드 문제가 여기서 해소됩니다.
  • 팔레트 양자화 — 수만 색의 출력을 캐릭터 시트에서 추출한 마스터 팔레트(기본 32색)로 수렴시킵니다. 프레임이 달라도 같은 팔레트를 공유하므로 애니메이션의 ‘일렁임’이 사라집니다.
  • 엣지 클린업 — 안티앨리어싱으로 뭉개진 경계를 제거하고 1px 아웃라인 규약을 복원합니다.
  • 투명 배경 추출 — 배경을 알파 채널로 분리해 어떤 씬에도 얹을 수 있는 상태로 만듭니다.

모션 스튜디오 — 이미 있는 그림도 움직인다

텍스트에서 시작하지 않는 경로도 있습니다. 레퍼런스 이미지 한 장을 드롭하면, 엔진이 그 그림의 스타일과 실루엣을 분석해 동일한 파이프라인 — 8방향 파생과 모션 확장 — 을 태웁니다. 오랫동안 갖고 있던 도트 캐릭터 한 장이 걷고 뛰는 애니메이션 세트가 되는 경험은 이 기능을 써본 사용자들이 가장 자주 언급하는 순간이기도 합니다.

Perfect Pixel 모션 스튜디오 — 레퍼런스 기반 모션 생성
모션 스튜디오 — 레퍼런스 이미지에서 파생된 모션 프레임들이 타임라인 단위로 정렬된다

멀티 프로바이더 — 특정 모델에 인질 잡히지 않기

이미지 생성 모델의 지형은 분기마다 바뀝니다. Perfect Pixel 엔진은 Gemini · OpenRouter · fal.ai · BytePlus 네 백엔드를 단일 인터페이스 뒤에 추상화했습니다. 프로바이더별 차이 — 레퍼런스 이미지 전달 방식, rate limit, 지원 해상도 — 는 어댑터 레이어가 흡수하고, 파이프라인 코드는 프로바이더를 모릅니다.

engine/provider.go (인터페이스)go
// 파이프라인이 아는 것은 이 인터페이스뿐 — 백엔드는 설정으로 교체된다
type ImageProvider interface {
    // anchor는 프레임 간 일관성을 위한 베이스 캐릭터 레퍼런스
    Generate(ctx context.Context, p CompiledPrompt, anchor *Image) (*Image, error)
    Capabilities() ProviderCaps // 해상도, 레퍼런스 지원, 동시성 한도
}

실전에서 이 구조는 두 번 우리를 구했습니다. 한 프로바이더의 정책 변경으로 특정 스타일 생성 품질이 급락했을 때, 설정 한 줄로 백엔드를 전환해 서비스 중단 없이 넘어갔습니다. 품질 루프의 판정 데이터가 프로바이더별 성능 벤치마크 역할을 해준 덕에, 어느 백엔드가 어떤 모션 카테고리에 강한지도 데이터로 알고 있습니다.

게임 엔진으로 — 내보내기 파이프라인

마지막 단계는 실제 게임 개발 워크플로에 꽂히는 일입니다. 완성된 프레임들은 모션 카테고리별로 정리된 스프라이트 시트로 패킹되고, 프레임 타이밍 메타데이터와 함께 내보내집니다. Unity · Godot · Phaser에서 별도 가공 없이 임포트할 수 있는 구조입니다. 아래는 Perfect Pixel로 만든 에셋이 실제 게임 씬에서 구동되는 모습입니다.

Perfect Pixel 에셋이 구동되는 게임 씬
생성된 스프라이트가 게임 씬에서 실시간 구동되는 모습
Perfect Pixel 내보내기 및 관리 UI
에셋 관리 UI — 모션 카테고리별 시트 구성과 내보내기 옵션

성능 벤치마크 — 숫자로 검증하다

엔진 초기 버전(단순 생성 → 사람 검수 방식)과 현재 버전(파이프라인 완전 자동화)을 직접 비교했습니다. 테스트 기준: 동일 캐릭터 명세(파란 갑옷 기사), 동일 프로바이더(Gemini 1.5 Flash), 100개 모션 풀 세트 생성.

15% → 2%
사람 재작업 비율
8x
파이프라인 병렬화로 향상된 처리 속도
~12분
100개 모션 풀 세트 생성 소요 시간
32색
기본 마스터 팔레트 — 리얼 픽셀라이즈 후 색 수

재작업 비율 15% → 2%는 생각보다 큰 차이입니다. 100개 모션 세트에서 15%면 15개를 손으로 다시 생성해야 했는데, 각 모션 재생성이 평균 3~5분이면 총 45~75분의 추가 작업입니다. 2%로 줄면 2개, 즉 6~10분으로 수렴합니다. 게임 프로토타입에서 캐릭터를 자주 바꿀 때 이 차이는 복리로 쌓입니다.

프롬프트 컴파일러 내부 — 한 문장이 명세가 되는 과정

사용자가 입력한 "파란 갑옷의 기사"는 컴파일러를 거쳐 아래와 같은 구조화된 명세로 확장됩니다. 이 명세가 모든 이후 생성의 계약서가 됩니다.

컴파일된 캐릭터 시트 명세 (발췌)json
{
  "input": "파란 갑옷의 기사",
  "compiled": {
    "subject": "판타지 기사 캐릭터",
    "visual_spec": {
      "armor_color": "코발트 블루 (#1a4fd6), 하이라이트 실버",
      "armor_style": "판금 갑옷, 어깨 보호대 돌출, 가슴 문장",
      "body_ratio": "5등신 픽셀아트 표준, 머리 1단위",
      "outline": "1픽셀 블랙 아웃라인, 안티앨리어싱 없음"
    },
    "palette_constraint": {
      "max_colors": 32,
      "required": ["#1a4fd6", "#0d2d7a", "#c0c0c0", "#000000"]
    },
    "pixel_grid": "32x48px 기준 격자",
    "lighting": "상단 좌측 45° 단일 광원"
  }
}

컴파일 과정에서 가장 중요한 결정은 팔레트 constraint를 먼저 확정하는 것입니다. 주요 색상 4개를 고정하면, 나머지 28색은 이 4색의 밝기/채도 변형으로 제한됩니다. 이 제약이 프레임마다 색이 달라지는 팔레트 폭발을 막는 핵심 장치입니다.

지원 게임 엔진 & 내보내기 포맷

내보내기는 세 가지 포맷을 지원합니다. 각 포맷은 특정 엔진의 임포트 파이프라인에 최적화되어 있으며, 메타데이터 파일을 함께 내보내 프레임 타이밍과 히트박스 정보를 담습니다.

  • PNG 스프라이트 시트 + JSON 메타 — Unity(SpriteEditor), Godot(SpriteFrames) 범용. 프레임 좌표와 FPS가 JSON으로 제공되어 추가 설정 없이 임포트.
  • Aseprite 호환 `.aseprite` — 픽셀아트 전문가가 Aseprite에서 열어 레이어 단위로 수정 가능. 컷 단위 레이어와 태그가 자동 생성됨.
  • Phaser 3 JSON Atlas — wasd(당사 텍스트-투-게임 엔진)와 직접 연동. scene.anims.createFromAseprite() 한 줄로 애니메이션 등록.

초기 설계의 실수들 — 그것이 지금 아키텍처를 만들었다

지금의 파이프라인이 처음부터 이 모습이었던 건 아닙니다. 초기 설계에서 틀렸던 것들을 기록해 둡니다. 같은 실수를 반복하지 않기 위해서이기도 하고, 이 기록 자체가 설계 결정의 근거가 되기 때문입니다.

  1. 고정 팔레트를 후처리에서 붙였다 (→ 실패) — 초기에는 생성 후 팔레트 양자화를 적용했습니다. 결과: 색 매핑 충돌로 캐릭터 색이 뭉개졌습니다. 팔레트는 생성 전에 프롬프트로 강제해야 한다는 결론.
  2. 모든 방향을 독립 생성했다 (→ 실패) — 8방향을 각각 별도 프롬프트로 생성하면 방향마다 장비 디테일이 달라집니다. 베이스 앵커 패턴으로 전환한 이후 일관성 문제의 80%가 해소됐습니다.
  3. 픽셀 크기를 고정했다 (→ 실패) — 32x48, 64x64 등 고정 캔버스를 강제했더니 복잡한 캐릭터는 선이 뭉치고 단순한 캐릭터는 여백이 과다했습니다. 지금은 캐릭터 복잡도에 따라 48x48~96x96 범위에서 컴파일러가 자동 결정합니다.
  4. 품질 판정을 사람이 했다 (→ 병목) — 100개 모션을 생성하고 사람이 골라내는 방식은 15%의 재작업이 발생했고, 배치 생성 후 하루 지나 결과를 확인하는 흐름이 반복됐습니다. AI 검수자를 파이프라인에 넣은 뒤 비동기 배치 생성이 완전히 자동화됐습니다.

마치며 — 생성이 아니라 파이프라인이 품질을 만든다

Perfect Pixel을 만들며 얻은 가장 큰 교훈은 이것입니다. 모델의 출력을 신뢰하지 말고, 검증하고 교정하는 구조를 신뢰하라. 프롬프트 컴파일러가 스타일 계약을 강제하고, 베이스 앵커가 일관성을 담보하고, 품질 루프가 불량을 걸러내고, 결정론적 후처리가 마지막 규약을 복원합니다. 각 단계는 단순하지만, 이 사슬 전체가 ‘문장 하나 → 프로덕션 에셋’이라는 경험을 만듭니다.

아트 팀 없이도 게임의 비주얼이 완성된다 — 이 문장이 마케팅 카피가 아니라 엔지니어링 명세가 되도록 만드는 것이 Perfect Pixel 엔진의 일입니다.

NEXUS AI Labs

Perfect Pixel은 현재 macOS 데스크톱 앱으로 제공되며, 오픈소스 버전도 공개되어 있습니다. 엔진의 다음 과제는 타일셋 · 배경 생성으로의 파이프라인 확장, 그리고 품질 루프 판정 데이터를 활용한 스타일별 자동 프로바이더 라우팅입니다.

더 읽어보기