3D 카메라 자막
After Effects 무브(3D 카메라, 피사계 심도, 패스 위 텍스트, 포스터라이즈 타임 15, 모션 블러)를 HyperFrames용 결정론적 프레임별 페인터로 재구성했습니다. 하나의 클록이 플레이트, 매트 및 모든 글리프를 페인팅합니다. CSS 애니메이션이 아닙니다.
사용 시기 / 사용하지 않을 시기
- 사용: 정적 카메라 토킹 헤드 테이크(아바타 또는 실제 인물), 가로, 미디엄 샷 또는 그 이상, 조각당 5–20초. 단어는 발화자 주변 깊이에서 표현되어야 합니다.
- 사용하지 않음: 일반 자막, 움직이는 카메라 또는 핸드헬드 촬영(매트와 플레이트 변환은 고정된 샷을 가정함), 극단적인 클로즈업(머리 뒤나 주위에 공간이 없음), 또는 인물 매팅 모델이 분리할 수 없는 피사체.
요구 사항 및 부작용 (전체 목록)
- 도구:
- Node 22+ 및 테스트된 버전으로 고정된 HyperFrames CLI,
npx [email protected](registry.npmjs.org에서 다운로드). 아래 모든 명령어에 대해 해당 정확한 버전을 사용하세요. - PATH에
ffmpeg. - Python 3 및
fonttools,brotli,numpy,pillow.
- Node 22+ 및 테스트된 버전으로 고정된 HyperFrames CLI,
- 매트:
npx [email protected] remove-background로컬에서 실행. 첫 실행 시~/.cache/hyperframes/에 사람 분할 모델(~170 MB)을 다운로드합니다. - 렌더링 시간: 컴포지션이 미리보기/렌더링 시
cdn.jsdelivr.net에서 GSAP 3.14.2를 로드합니다. - 워드 클록: 사용자가 이미 로컬에서 실행 중인 모든 단어 수준 전사기. 온셋 정확도가 중요합니다. 일반 Whisper는 시작이 늦게 실행됩니다.
- 폰트: 사용자 제공 파일. 라이선스에 포함이 허용되는지 확인하세요.
- 손글씨 스타일(선택 사항):
p5-paint-animation스킬이 설치 및 설정되어 있어야 합니다(설치 시 고정된 puppeteer, Chrome for Testing, p5 및 p5.brush를 다운로드함).build-sprites.py가 헤드리스로 실행합니다. 프레임은 해당 스킬의out/에 기록된 후 제거됩니다. - 그 외: 자격 증명, 유료 호출, 업로드 없음. 파일은 프로젝트(
assets/,renders/)에만 기록됩니다.
준비 (테이크당 한 번)
- 포션 및 플레이트.
bash scripts/prep-take.sh <take.mp4> <start_s> <dur_s> <project>/assets는 다음을 생성합니다:portion.mp4: 오디오 소스.plate-tall.mp4: 각 측면 240px, 상단 480px, 하단 240px 반사 패딩되어 휩, 트럭 및 풀백 시 가장자리가 보이지 않도록 함.person.webm: 알파 매트, 동일한 프레임.- 그런 다음 스크립트는 플레이트 및 매트를 포션(크기, 프레임 속도, 프레임 수)과 비교하여 불일치 시 실패합니다.
- 워드 클록. 포션의 단어 타이밍(초 단위, 시작부터).
- 폰트. 2–3개 역할(자막 / 히어로 / 링) 선택 후
python3 scripts/font-metrics.py assets/metrics.js key=font.woff2[@wght=…,opsz=…] …실행.- 가변 폰트는 축을 여기에 전달하고 CSS에서 고정해야 함(
font-variation-settings,font-optical-sizing: none). 그렇지 않으면 브라우저의 opsz가 크기를 따라 단어가 겹칩니다.
- 가변 폰트는 축을 여기에 전달하고 CSS에서 고정해야 함(
- 실루엣. 배치 전에
person.webm알파에서 머리 상자와 어깨선을 읽습니다.
빌드
assets/kit/{cam3d.js,tables.js,finish.js}및metrics.js를 프로젝트의assets/에 복사합니다.references/worked-film.html은 완전한 참조 필름입니다. 두 개의 샷, 바디 와이프, 두 링 유형, 플라이 스루, 피날레 및 그레이드가 포함되며 미디어는 포함되지 않습니다.references/recipes.md에는 깊이 우선 그룹, 래핑된 링, 계단 및 손글씨 단어가 있습니다.C3.init(metrics, {W, H}):- 렌즈는 튜토리얼의 수직 시야(F = 1326.1·H/1080)를 유지하므로
tables.js는 모든 너비에서 스케일 없이 적용됩니다. - 1440보다 넓은 프레임에서는
(W − 1440) / 2를wipe.x에 추가합니다.
- 렌즈는 튜토리얼의 수직 시야(F = 1326.1·H/1080)를 유지하므로
샷 문법
4–6개의 비트를 선택합니다. 각 음성 절을 자체 그룹으로 나눕니다.
| 비트 | 빌드 | 규칙 |
|---|---|---|
| 휩인 펀치인 | camA 테이블(502px 낮음에서 19f) 후 linear 크립 | 플레이트 S0 1.1–1.3; 프레임은 절대 안착하지 않음 |
| 깊이 그룹 | 절당 2–3개의 자막 그룹, D 1250 / 1500 / 2200 | 가까울수록 더 크고 큰 그림자; 크립과 트럭이 시차를 드러냄 |
| 히어로 뒤 | 플레이트 깊이의 뒤 레이어에 히어로 단어, 머리 위 마스트헤드 스타일 | 머리가 중간 글자를 가림; ≥ 60% 가독성 유지; 어센더가 프레임 내에 있음 |
| 시차 트럭 | swing 세그먼트, tx 420 | 이전 레이아웃이 3단계에 걸쳐 페이드; 다음 레이아웃이 시차로 도착 |
| 바디 와이프 | 발화자 자신의 매트, brightness(0) blur(), wipe.x에서 앞쪽 가장자리 | 샷 A는 밴드 왼쪽, 샷 B는 오른쪽 클립; 양쪽에서 풀아웃 |
| 래핑된 링 | 발화자 앞의 수평 원(중앙 Z 2000), 위에서 11–16°, 회전 | 단어가 말해지면서 측면에서 작게 들어와 앞쪽으로 스윙, 안착; 아래쪽 아크는 뒤집혀서 읽기 쉽게 |
| 플라이 스루 | 구문이 D 1500 / 2000 / 2500, 카메라가 그 사이를 스윙 | 포커스가 구문을 따라감 |
| 계단 피날레 | 푸시 세그먼트; 각 자막 단어가 대각선 아래로 더 깊고 작아짐 | 푸시가 가까운 단어를 분리시킴 |
규칙
- 모션:
- 텍스트는 15fps로 포스터라이즈됨(자체 애니메이션과 보이는 카메라 모두). 플레이트와 매트는 매 프레임 움직임.
- 모션 블러는 15fps 스텝의 절반에 걸쳐 7개의 고스트. 회전 링에는
shutter: 0.5사용. - 자막은 음성보다 0.2초 앞서며 15fps 그리드에 스냅됨:
fr(t) = 2·round((t − 0.2)·15). - 진입은
ENTRY만큼 상승: 자막 30px, 히어로 110–260px. 링 단어는 경로를 따라 마지막 30–44px을 이징.RING_ENTRY의 오버슈트는 이웃이 아직 안착하지 않았을 때만 사용.
- 깊이:
- 모든 그룹은 자체 D를 가짐. 플레이트 3000, 자막 1250–2400, 래핑된 링 ≈ 1100–2700.
- 깊이 그림자
[6, 16, 0.5, 1500]는 1500/(D − cz)로 스케일됨. - 피사계 심도 σ = 6.7·|D − focus|/D px (1080 높이 기준). 평면 자막은 1.2px로 제한; 링 글리프는 전체 블러를 받음.
- 크기:
size/rise는 착륙 카메라(tland)에서의 화면 px. 스프라이트는 화면 크기로 렌더링됨. - 간격:
- 링 텍스트는 항상
ringLine을 통과함(화면 전진; 타입: 트랙 1.10, 갭 26–30; 손글씨: 트랙 1.0, 갭 28). - 회전 링은 매 포즈마다 다시 배치됨.
- 링 텍스트는 항상
- 폐색:
- 링은 발화자 앞에 그려짐.
- 단어를 뒤/앞 인스턴스(
side,zSplit)로 분할하는 것은 머리 뒤를 통과하도록 의도된 경우에만.
- 가독성:
- 자막은 프레임의 어두운 쪽에 배치.
- 피부나 밝은 옷을 교차하는 텍스트는 어두운 후광을 가짐.
- 화면에 마침표 없음.
- 그레이드 (선택 사항):
finish.js가 곡선, 채도 1.12(피부용), 및 프레임당 재롤링된 그레인을 제공. 불투명 장면(플레이트 + 뒤 텍스트 + 매트)을 필터링된 래퍼에 배치. 앞 텍스트에 동일한 그레이드와 그레인을 합성하여 위에 배치. 따라서 뒤/앞으로 분할된 단어는 하나의 색상을 유지함(references/worked-film.html참조).
스타일
- 에디토리얼: 압축된 디스플레이 세리프, 히어로는 이탤릭; 자막용 텍스트 세리프; 크림색과 하나의 악센트.
- 볼드: 무거운 그로테스크 히어로, 가벼운 자막, 하나의 채도 높은 악센트(작업 필름).
- 손글씨:
scripts/hand/build-sprites.py의 p5.brush write-on 스프라이트:- 각 단어는 온셋부터 스스로 쓰기 시작.
- 링은 단어를 전체적으로 배치, 접선으로 회전하고 깊이로 스케일, 더 크고 평평한 링 위에.
- 레시피는
references/recipes.md에.
검증
npx [email protected] check . --no-contrast. 고스트 레이어가 WCAG 대비 검사를 트리거함; 휩 및 와이프 이음새는 의도적으로 겹침.- 미리보기 페이지에서
scripts/frame-bounds.js를 붙여넣고__frameBounds(0, dur)실행.[]를 반환해야 함: 5+ 스텝 동안 보이는 상태에서 프레임 밖의 그룹 없음.
scripts/glyph-gaps.js를 붙여넣고 정지 상태에서__glyphGaps(t)실행.- 단어 간 접촉 없음.
- 평면 단어는 하나의 상자로 측정됨(descenders는 false positive 플래그). 이탤릭 f의 오버행은 음수로 읽힘. 와이프 밴드 반대쪽에 있는 단어는 둘 다 표시됨.
- 단어 간격: 단어 사이의 렌더링된 간격 ≈ 공백 하나 전진.
- 각 비트의 스냅샷,
npx [email protected] render . --crf 12로 렌더링, 비트당 전체 해상도 프레임 확인.
한계
- 미세한 머리카락에서 매트 가장자리가 깜빡임. 반사 패드가 휩 중에 잠시 보임.
- 프레임을 채우는 발화자는 뒤에 공간이 거의 없음: 마스트헤드 높이로 가거나 어깨 뒤에 숨김.
snapshot --at은 하나의 페이지를 공유하므로 캡처 간 그레인이 재롤링되지 않음. 렌더에서 그레인 평가.- 그레이드와 많은 고스트 레이어로 렌더링하는 것은 일반 렌더의 약 2배 비용.