---
title: 엔진 표정 개선 사양서 — 뷰어 실험·리서치에서 도출한 엔진 반영 스펙
date: 2026-07-13
type: spec
audience: Engineering (V3 Face 엔진)
status: v1 (제안)
related: "[[face-expression-experiments-report-20260713]] [[anime-expression-key-elements-20260713]] [[disney-pixar-expression-key-elements-20260713]] [[talking-face-dynamics-mismatch-20260713]]"
---

# 엔진 표정 개선 사양서 (2026-07-13)

> [!abstract] 이 문서의 용도
> 7/11–13 뷰어 실험(리서치 6건 + 후처리 레이어 12종, 전부 라이브 수치 검증)에서 도출된 결과를 **엔진이 흡수할 수 있는 사양**으로 번역한 것이다. 근본 동기: 후처리는 채널별 독립 조작이라 **채널 간 상관 구조를 훼손**한다 — 같은 효과를 엔진(모델 출력 또는 모델 내장 후처리) 단계에서 내면 상관이 보존되어 자연스러움을 잃지 않는다. v14→v18m(표정 증폭+크로스페이드를 모델 쪽에서 해결)이 이 접근의 사내 실증 사례다.
>
> 각 항목은 **근거 → 목표 스펙(수치) → 엔진 반영 옵션 → 검증 방법** 순서로 기술한다. 뷰어 구현(`package/face/animasync-face-v3/tools/blendshape-player-natural.html`)이 전 항목의 레퍼런스 구현이며, 상수·함수명을 병기했다.

**표기**: 채널 번호는 ARKit 52 인덱스. (0/1 browDown L/R, 2 browInnerUp, 3/4 browOuterUp L/R, 6/7 cheekSquint, 8/9 eyeBlink, 10–17 eyeLook, 18/19 eyeSquint, 20/21 eyeWide, 24 jawOpen, 27/28 dimple, 29/30 frown, 43/44 smile, 45/46 stretch, 49/50 noseSneer)

---

## A. 스타일 조건화 출력 (최상위 제안)

**근거**: 지각 연구의 일관된 결론은 "정합성 > 사실성" — 외형(애니 캐릭터)과 모션 스타일이 맞아야 한다(Mäkäräinen 2014). 뷰어에서 Anime/Disney 스타일을 후처리로 입혀 가독성은 올렸지만 상관 훼손 트레이드오프가 구조적으로 남는다.

**목표**: 엔진이 스타일 태그(예: `style: natural | anime | disney`)를 조건으로 받아, 스타일별 채널 상관·타이밍이 **모델 출력 자체에** 반영된 blendshape 스트림을 낸다.

**반영 옵션** (비용 오름차순):
1. **출력단 후처리 내장** — 아래 B–E의 규칙을 infer 파이프라인(crisp_mouth처럼)에 스타일 프리셋으로 내장. 뷰어와 동일 로직이지만 배포 일관성 확보. 상관 훼손 문제는 남음.
2. **학습 데이터 스타일 라벨링** — 기존/신규 데이터에 스타일 라벨을 붙여 조건부 생성 학습. 애니풍 연기 데이터가 필요하다면 Vicon 촬영 계획(VAD 강도 지시)과 연결. 상관 보존의 정공법.
3. **혼합** — 진폭·감정 강도는 학습(2), 타이밍 양식(스냅·스프링)은 출력단(1). 타이밍은 결정론적 규칙이라 후처리로도 상관 훼손이 적다(단일 시간축 변형이므로).

**권고**: 3번 혼합. 진폭·상관은 모델이, 타이밍 양식은 출력단이.

## B. 감정 진폭 — 엔진이 약하게 내는 감정

**근거**: 일상 대화 데이터 특성상 감정 채널이 저진폭(눈썹 0.2–0.5 대역)에 머문다. 뷰어에서 두 단계로 해결했고 둘 다 유효성이 실증됐다.

**B-1. 채널별 차등 게인** (뷰어: `EXAG_W`, 감정 솔로클립 4종 p95 실측으로 도출)

| 가중치 | 채널 |
|---|---|
| 1.0 | browDown(0/1), browInnerUp(2), browOuterUp(3/4), cheekSquint(6/7), eyeWide(20/21), frown(29/30), noseSneer(49/50) |
| 0.9 | smile(43/44) |
| 0.8 | dimple(27/28) |
| 0.6 | eyeSquint(18/19) |
| 0.5 | press(23/22?) · 0.4 stretch(45/46) · 0.3 cheekPuff |
| **0 (불변)** | jawOpen(24)·비젬 전체·blink(8/9)·gaze(10–17) — 립싱크·시선 보호 |

부속 규칙: 활성 램프(값 0.02–0.12에서 게인 페이드인 — 노이즈 증폭 방지), tanh 소프트클립(0.75 이상 — 포화 뭉갬 방지). 뷰어: `exaggerate()`, `softClip()`.

**B-2. 감정 원형 주입** (뷰어: `EMO_RECIPES` + `_emoSig`)
- 감지: joy=smile/cheek 램프, sad=browInnerUp(0.20–0.42 조기 포화)×frown(0.08–0.25), anger=browDown(0.16–0.42), surprise=eyeWide(0.12–0.32). EMA 상승 150ms/하강 500ms. **시크/불연속 시 신호를 해당 프레임 값으로 즉시 스냅**(잔류 방지 — 실측 버그였음).
- 주입: 감지 신호 × 원형 레시피를 max()로 floor-lift. 레시피 수치:
  - joy: smile .95, cheekSquint .6, dimple .45
  - sad: browInnerUp .95, frown .8, browDown .1, eyeLookDown .3
  - anger: browDown 1.0, noseSneer .55, eyeSquint .45, frown .4, press .45
  - surprise: browInnerUp .55, browOuterUp .95, eyeWide .9, jawOpen .35
- **설계 함정(중요)**: 감지 문턱과 주입 목표를 같은 수준으로 잡으면 순환 무효과. **감지는 조기 포화, 주입은 원형 강도**로 분리.

**엔진 반영**: 옵션 2(학습)라면 이 레시피가 "스타일별 감정 피크의 목표 분포"가 된다. 데이터 수집 시 감정 강도 지시(VAD)로 피크 진폭을 원형 수준까지 끌어올린 테이크 확보가 관건.

**검증**: 감정 솔로클립에서 감정 운반 채널 p95가 레시피의 ±0.1 이내, jawOpen·비젬 분포는 natural 대비 불변.

## C. 말의 문법 층 — "대화하는 얼굴"

**근거**: 사람 대화 얼굴 신호의 64.5%는 감정이 아니라 화용·구조 기능이다(Bavelas & Chovil). 순수 감정 매핑만으로는 말하는 얼굴이 안 나온다.

**목표 스펙**:
1. **눈썹–운율 결합**: 눈썹 올림이 피치 액센트를 **60ms 선행**(Flecha-García). 강세 음절·담화 세그먼트 시작점에 눈썹 이벤트. 뷰어는 DoG 밴드패스(100–267ms 대역)로 근사 중(brow prosody) — 엔진은 오디오 피처(F0)에서 직접 학습 가능한 항목.
2. **화자/청자 모드**: 청자 모드(back-channel: 끄덕임·미소·눈썹 플래시)는 별도 상태. 현 엔진은 화자 얼굴만 — 대화형 제품(컴패니언)에선 청자 얼굴이 절반이다.
3. **침묵 구간도 정지 금지**: 침묵 중 얼굴 동역학만으로 대화 유형을 82% 분류 가능할 만큼 정보가 있다(Sci Rep 2025). 발화 없는 프레임의 출력이 평평하면 데이터/모델 문제.

**검증**: 엔진 출력에서 눈썹 이벤트와 오디오 피치 액센트의 시간 상관(선행 40–80ms 대역 피크), 침묵 구간 AU 분산이 0이 아닌지.

## D. 시간 구조 — 전환·홀드의 양식

**근거**: 사람 표정은 4위상 램프(neutral→onset→apex→offset)로 켜지고 꺼진다. 애니는 전환을 감정 비트에 몰고(2–3프레임 전환+8프레임 이상 홀드), 디즈니는 overshoot+settle 곡선으로 움직인다. 완전 정지(dead hold)는 어떤 스타일에서도 금지(Tinwell 2011 — 발화 중 윗얼굴 고정 자체가 uncanny).

**목표 스펙 (스타일별)**:

| 항목 | natural | anime | disney |
|---|---|---|---|
| 전환 | 4위상 램프 (엔진 학습 그대로) | 온셋 이벤트에서만, 100–150ms 램프 | 저감쇠 스프링 f=2.5Hz, ζ=0.6 (오버슈트 약 9.5%) |
| 홀드 | 없음 (연속) | 온셋 간 홀드 + 3s 시정수 누수 | 없음 (스프링이 연속 추종) |
| 예비동작 | — | — | 스프링 r항 < 0 (큰 변화 직전 반대 딥 3–5%) |
| 무빙 홀드 | — | 홀드 중 ±2% @ 0.5Hz | (스프링 미동이 대체) |
| 프레임 질감 | 30fps | 감정 채널 12fps 스텝(선택) | 30fps |

- **온셋 정의** (뷰어 실증): 감정 신호의 빠른 EMA(τ=150ms)와 느린 그림자(τ=600ms)의 발산 > 0.10 (상승률 약 0.22/s 이상에서 발화), 히스테리시스 0.05 재장전, 불응기 1.2s. 프레임 단위 델타 방식은 실데이터에서 한 번도 발화하지 않음(실측 최급 상승 0.134/333ms) — 사용 금지.
- **엔지니어링 원칙**: 시간축 변형(스프링·홀드)은 반드시 **파이프라인 최종 단계**에. 중간에 두면 후단 처리가 매 프레임 재요동시킨다(뷰어에서 2회 반복 실증).

**검증**: anime 스타일 출력에서 감정 채널의 프레임 간 |Δ| 평균이 natural 대비 50% 이상 감소하되 0이 아님(무빙 홀드), disney 스타일에서 스텝 응답 오버슈트 8–12%.

## E. 눈 — 감정 연기와 생명 신호

**근거**: 절차적 눈 움직임이 "대기 전용"이면 감정이 눈에서 안 읽힌다. 아래 전부 뷰어 실증 완료.

**E-1. Blink 운동학** (뷰어: `blinkEnvelope`, `regenBlinkSchedule`)
- 프로파일: 감기 90ms(ease-in) → 홀드 35ms → 뜨기 200ms(ease-out). **감기:뜨기 = 1:2 비대칭.**
- Partial blink 50%(진폭 0.6–0.8), double blink 8%.
- **발화 의존**: 대화 중 10.5–32.5회/분 vs 집중 시 약 4.5회/분. 발화 구간 ×1.5 / 침묵 ×0.5 (jawOpen 활성 ±0.4s 창으로 발화 판정). 실증: 발화 35% 클립에서 blink 80%가 발화 구간에 군집.
- **반응 깜빡임**: 감정 온셋(D의 온셋 정의)에서 1회 발화 — "blink on the thought" 애니메이터 관행.

**E-2. 사카드** (뷰어: `regenGazeSchedule`, `gazeAt`)
- Main sequence: D(ms) = 2.2×A(deg) + 21 (풀 편향 40° 가정).
- 방향 가중 수평:수직:대각 = 5:3:2. Fixation 지수분포 평균 330ms(하한 200ms).
- 오버슈트 3–8% + glissade 세틀. 9° 이상 점프의 55%에 blink 커플링.

**E-3. 감정 시선**
- 슬픔 → eyeLookDown floor 0.35×신호. 놀람·분노 → 응시 고정(예정 blink·사카드·동공 미동을 신호 유지 동안 억제 — 크게 뜬 눈/노려보기가 잔움직임에 씻기지 않게).

**엔진 반영**: blink·gaze는 엔진 출력 채널(8/9, 10–17)이므로 학습 대상이 될 수도, 런타임 절차 레이어로 남길 수도 있다. **권고: 절차 레이어 유지하되 엔진 SDK에 표준 구현으로 포함**(위 수치 그대로) — 결정론적 운동학이라 학습보다 규칙이 정확하고 저렴.

## F. 출력 채널 확장 요구 (ARKit 52의 한계)

**근거**: 애니 감정의 최고 레버가 ARKit 52에 없다. 후처리 근사의 품질 상한이 여기서 결정됨.

| 필요 채널 | 용도 | 현 근사(한계) |
|---|---|---|
| 호선눈(HappyArc/SadArc) | 기쁨·곤란의 눈 감정 | blink 85%로 근사 — 실루엣이 다름 |
| 동공 스케일(IrisShrink) | 충격·공포 | 불가 |
| 하이라이트 알파/스파클 | 생기·감동 | 불가 (머티리얼 파라미터) |
| 홍조(Blush) | 수줍음 | 불가 (텍스처/머티리얼) |
| EyeSqueeze(><) | 웃음 폭발 | blink 100%로 근사 |

**제안**: 엔진 출력 포맷에 **스타일 오버레이 트랙**(기존 stylized_overlay 논의와 정합)을 추가 — ARKit 52 + 확장 N채널. 아트 사양은 이미 존재(한나 문서: GG 캐릭터 렌더 12종). 아바타 쪽 모프 심기는 아트 트랙(P2)과 동기화.

## G. 출력 품질 가드 — 실측 상한

**근거**: 같은 부위를 변형하는 채널 쌍은 합산 시 파손된다(눈밑 찌그러짐). 엔진 출력이 이 상한을 넘으면 아바타에서 아티팩트.

- cheekSquint 자연 최대 **0.66**, eyeSquint **0.80** (엔진 실측 max).
- 쌍 합(cheekSquint+eyeSquint, 좌우 각각) p99 = **1.46**. 뷰어 가드: 기본 1.3, Duchenne가 필요한 disney 스타일만 1.45. 초과 시 eyeSquint 먼저 컷(홍채를 뭉개는 건 눈꺼풀 채널).
- **활용**: 학습·추론 QA 지표로 — 출력 분포가 이 상한을 넘는 프레임 비율을 회귀 테스트에 포함 권장.

## H. 우선순위 제안

| 순위 | 항목 | 근거 |
|---|---|---|
| P0 | B(진폭·원형) 학습 데이터 반영 — 감정 강도 지시 촬영 | Vicon 계획과 즉시 연결, 상관 보존의 정공법 |
| P0 | E(blink·사카드 운동학) SDK 표준화 | 결정론적, 저비용, 모든 제품에 즉효 |
| P1 | D(스타일 타이밍) 출력단 프리셋 | 뷰어 로직 이식이면 됨, 스타일 데모 품질 직결 |
| P1 | C-1(눈썹–운율) 학습 피처 검토 | 오디오에서 직접 학습 가능한 항목 |
| P2 | F(채널 확장) + 아트 모프 | 아트 트랙 의존, 애니 스타일 완성의 필요조건 |
| P2 | C-2(청자 모드) | 컴패니언 제품 로드맵과 정렬 |

## 참조

- **레퍼런스 구현**: `package/face/animasync-face-v3/tools/blendshape-player-natural.html` — 상수: `EXAG_W`·`EMO_RECIPES`·`DS_W/DS_Z/DS_K1`·`BLINK_CLOSE/HOLD/OPEN`·`LOWER_LID_CAP(_DISNEY)`·`ANIME_SNAP_MASK` / 함수: `exaggerate`·`exagLowerLidPairs`·`regenBlinkSchedule`·`regenGazeSchedule`·`speechActiveAt`·온셋 감지(applyFrame 내 `_emoSlow` 블록)
- **리서치 원문**: `pages/research/anime-expression-key-elements-20260713.md` · `disney-pixar-expression-key-elements-20260713.md` · `talking-face-dynamics-mismatch-20260713.md`
- **실험 종합**: `pages/research/face-expression-experiments-report-20260713.md` (팀 공유용, PDF 있음)
- **주요 커밋**: `d77d1bd`(Anime+운동학) `c81c5b7`(감정 증폭) `40ed620`(Disney 스프링) `cada3b7`(스타일 강조) `17c3559`(온셋 감지) `0db10b5`(비트 홀드) 외 — 상세는 실험 종합 리포트 부록 B
