아바타 v3 표현 엔진·리노컷 렌더러·립싱크와 Lab

- 표현 엔진: 채널 34개(mouthRound 추가), 표정 28종·강도 곡선·반응 클립 26종·지문 cue, 미세표정 누설
- 발화층: 한글 자모 비짐 9종, TTS 오디오 선분석 정렬, 60ms 앞당김·모음 간 비닫힘
- 발화 동반층: 억양·강세에 맞춘 고개 표류·끄덕임·질문 갸웃·들숨·눈썹 박·시선 회피·쉼 깜빡임
- P1 서연 리노컷 렌더러: 원화 픽셀 입술 띠 변형, 하관 띠 변형(턱·코 들썩), 볼 불룩, 작은 크기 선 보정, 모티프·배경 색면
- /dev/avatar-lab, check:avatar-presets·check:avatar-lipsync, avatar-lab E2E 19개
This commit is contained in:
Yun Chan 2026-10-01 09:58:00 +09:00
parent b7bd24f016
commit 85bd079d18
47 changed files with 8310 additions and 0 deletions

View file

@ -0,0 +1,47 @@
/* =====================================================================
아바타 v3 엔진 전용 시드 난수 — mulberry32.
엔진은 순수 함수여야 하므로(결정문 §3) Math.random을 직접 쓰지 않고
이 모듈이 주입하는 rng를 통해서만 난수를 소비한다.
===================================================================== */
export interface Rng {
/** [0,1) 균등 분포. */
next(): number;
/** [a,b) 균등 분포. */
range(a: number, b: number): number;
/** 배열에서 균등하게 하나 고른다. */
pick<T>(arr: readonly T[]): T;
}
export function createRng(seed: number): Rng {
let state = seed >>> 0;
function next(): number {
state = (state + 0x6d2b79f5) | 0;
let t = state;
t = Math.imul(t ^ (t >>> 15), t | 1);
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
}
return {
next,
range(a: number, b: number): number {
return a + (b - a) * next();
},
pick<T>(arr: readonly T[]): T {
const idx = Math.min(arr.length - 1, Math.floor(next() * arr.length));
return arr[idx];
},
};
}
/** FNV-1a 32bit 문자열 해시. */
export function hashString(text: string): number {
let hash = 0x811c9dc5;
for (let i = 0; i < text.length; i++) {
hash ^= text.charCodeAt(i);
hash = Math.imul(hash, 0x01000193);
}
return hash >>> 0;
}