코드 속의 영화 (Film in code)
영화는 하나의 프로그램입니다. 모든 프레임은 코드로 그려지는 시간의 순수 함수이며, 음성, 음악, 그리고 모든 소리도 코드에서 만들어집니다. 이미지나 비디오 생성기는 전혀 사용되지 않습니다. 따라서 어떤 순간이든 변경하고 재렌더링할 수 있으며, 다른 언어는 단순히 재실행일 뿐, 새로운 편집이 필요하지 않습니다.
이 파이프라인으로 러시아어와 영어로 된 5분 16초 분량의 영화를 제작했습니다: 16개의 비트(beats), 57개의 큐(cues), 10개의 3D 삽입 장면, 스크립트에서 첫 번째 언어까지 약 1.52시간, 노트북에서 언어별로 1416분의 렌더링 시간이 소요되었습니다. 해당 영화의 코드는 assets/examples/court-astronomer/에 있습니다.
핵심 아이디어: 음성 우선 (Voice first)
마커가 포함된 스크립트를 작성합니다. 음성 서비스가 이를 읽고 각 단어의 시간을 보고하게 합니다. 그러면 마커가 절대적인 초(second)로 변환되며, 그림과 소리는 모두 그 초에 종속됩니다.
Придворные астрономы {plant}поставили её на карту и {cut}записали дату.
CUE.plant = 9.21 손이 별을 내려놓는다: 툭, 불꽃
CUE.cut = 10.50 문서로 전환, 깃털펜이 쓰기 시작
각 언어는 자체 녹음에서 얻은 고유한 타임라인을 가지므로, 러시아어와 영어 영화는 추가 작업 없이 다르게 편집되며, 문장을 다시 쓰면 그 뒤의 모든 것이 자동으로 이동합니다. 이것이 아래 순서가 스크립트, 음성, 그림, 소리 순서이며 절대 그림이 먼저 오지 않는 이유입니다.
script/script.json ─ tools/tts.py ─> audio/voice/<lang>/ + web/timing_<lang>.js
│
┌─────────────────────────────────────────┴───────┐
web/film.js + shots + space.js audio/score.py
│ render.py │
renders/film_<lang>/film_<lang>.mp4 audio/mix/<lang>.wav
└───────────────── tools/finish_film.py ──────────┘
final/<stem>.mp4, -phone.mp4, .srt
프로젝트 시작하기
python <this-skill>/scripts/check_env.py
python <this-skill>/scripts/new_project.py <folder>
프로젝트는 용량이 충분한 드라이브에, 영화 이름을 딴 폴더에, 짧은 경로에 생성하세요. 프레임은 인코더로 스트리밍되어 저장되지 않으므로, 5분짜리 영화는 약 1 GiB의 공간이 필요합니다.
새 프로젝트는 달의 위상에 대한 25초짜리 작동하는 데모를 두 가지 언어로 제공합니다. new_project.py가 출력하는 명령어를 실행하고, 무엇이든 변경하기 전에 연락 시트(contact sheet)를 확인하세요: 데모가 작동한다면, 이후의 모든 실패는 기계가 아닌 여러분의 책임입니다.
템플릿의 상태. 이 파이프라인은 실제 영화를 제작했지만, 이 템플릿은 그 이후에 추출되었습니다. 데모에서 tts.py, 믹스(mix) 및 음향 검사는 실행되었습니다. 전달 도구는 검은 클립에 대해 실행되었고, render.py는 스텁 페이지에 대해, 그리고 평면 그림 로직은 모든 프레임에 대해 스텁 캔버스에서 두 언어로 실행되었습니다. 템플릿은 실제 프레임을 그린 적이 없으며, 3D 샷도 실행된 적이 없습니다. 따라서 첫 번째 스틸 이미지가 템플릿의 테스트이기도 합니다: 실패한다면, assets/examples/court-astronomer/와 비교하세요. 이곳이 실제 렌더링된 코드입니다.
그런 다음 콘텐츠를 교체하고 기계장치는 그대로 유지하세요:
| 각 영화마다 교체 | 일반적으로 그대로 둠 |
|---|---|
script/script.json | tools/ |
web/shots.js (평면 샷) | render.py, server.py |
web/film.js: SHOT 및 SEQ | web/main.js, web/lib.js, web/labels.js |
web/space.js: 빌더(builders) | web/space.js: 그 위의 키트(kit) |
audio/score.py | audio/mixlib.py, audio/synth.py |
워크플로우
2분 이상 걸리는 명령어는 백그라운드에서 실행하세요. 시스템에 작업 큐나 열 관리 규칙이 있다면(프로젝트 노트나 메모리 참조), 렌더링을 통해 실행하세요.
1. 요청 확정하기
기본값으로 설정할 수 없는 것만 물어보세요: 주제, 언어, 게시 위치입니다. 기본값은 1920x1080, 24fps, -15 LUFS, 스크립트에 따른 길이입니다. 어떤 부분도 다듬기 전에 완전한 러프 컷(rough film)을 먼저 만드세요. 사람들은 완벽한 10초보다 전체 영화를 훨씬 더 잘 평가하며, 종종 최고 품질은 기다려도 된다고 말합니다.
2. 참고 자료가 있다면 읽기
추측하지 말고 측정하세요: 컷, 리듬, 팔레트, 캐릭터 업데이트 빈도, 선 두께, 그레인(grain)입니다. 비디오를 다운로드하지 않고 브라우저에서 프레임별로 수행할 수 있습니다. references/reference-analysis.md를 읽고, 숫자를 프로젝트의 README에 짧은 스타일 시트로 기록하세요.
기법은 가져가고, 콘텐츠는 그대로 두세요. 고유한 캐릭터, 스토리 및 디자인을 창작하세요. 참고 자료의 프레임, 캐릭터, 로고 또는 알아들을 수 있는 멜로디를 재사용하지 말고, 새로운 이름으로라도 특정 저작물을 재현하지 마세요.
3. 10초짜리 시험 영상 만들기
최종 스타일의 한 컷, 무음입니다. 여기서는 하드코딩된 시간을 사용해도 됩니다. 이는 스타일만 테스트하기 때문입니다. 사용자에게 보내세요. 이것은 전체 프로젝트에서 방향을 바꾸기에 가장 저렴한 순간입니다.
4. 사실 수집, 스크립트 작성
references/script-and-voice.md를 읽으세요. 영화를 10~35초 길이의 비트(beat)로 나누고, 각 비트는 하나의 아이디어이며 일반적으로 하나의 샷입니다. 단어 수를 계획하세요: 러시아어는 분당 약 125단어, 영어는 150단어이며, 일시 정지를 포함합니다. 화면에서 어떤 일이 발생해야 하는 단어에 큐(cue)를 배치하세요.
정확한 사실과 해석을 영화 내에서, 그리고 노트뿐만 아니라 시각적으로 분리하여 유지하세요. 무언가를 계산한다면, 영화가 읽는 파일에 데이터를 보관하여 화면에 표시되는 것이 데이터가 말하는 것과 일치하도록 하세요.
주제가 민감하거나 사용자가 승인을 요청한 경우 스크립트를 보여주세요. 그렇지 않으면 계속 진행하고 사용자가 영화에 반응하도록 하세요.
5. 음성, 그 다음 타임라인
python tools/tts.py
각 큐를 name=word@seconds 형식으로 출력합니다. 그 줄들을 읽으세요: 잘못된 단어에 있는 큐는 여기서 잡는 것이 훨씬 저렴합니다. 총 시간을 목표와 비교하세요. 너무 길면 텍스트를 줄이세요; 음성 속도를 약 +10% 이상으로 높이지 마세요.
6. 샷 작성하기
references/picture.md를 읽으세요. 화면의 모든 것은 (g, t)의 함수이며, 모든 시간은 CUE 또는 BEAT에서 파생됩니다. 평면 샷(flat shots)은 Canvas 키트로 그립니다; 3D 샷은 컴포저(composer)를 통해 동일한 2D 캔버스로 렌더링됩니다. 모든 프레임 끝에 하나의 그레인 패스(grain pass)와 한 세트의 라벨이 두 가지를 하나의 영화처럼 보이게 합니다.
7. 스틸 이미지 확인, 수정, 반복
python render.py renders/check "lang=ru×=25,66,241,273"
python tools/contact.py renders/check 4
references/review-and-render.md를 읽어 어떤 순간을 선택하고 무엇을 봐야 하는지 알아보세요. 시트를 열고 진짜로 검사하세요: 대부분의 결함은 코드에서는 보이지 않지만 그림에서는 명확합니다. 질문을 모아서 하세요. 연속적인 리뷰 렌더링은 노트북을 뜨겁게 만들고 세션을 느리게 만듭니다.
8. 사운드 작성하기
references/sound.md를 읽으세요. 섹션은 음성 비트에서 시작하고, 효과는 그림과 동일한 큐 이름에 위치하며, 음악은 음성 아래에서 덕킹(duck)되고, 믹스는 -15 LUFS에 도달합니다.
python audio/score.py ru
python tools/bands.py ru
python tools/qa_audio.py ru whisper
소리를 들을 수 없으므로 숫자를 사용하세요. 모든 비트에서 음성이 나머지보다 약 10dB 높은 것을 목표로 하세요; 실제 영화에서는 6.714.2dB, 대부분 79dB였으므로 이 범위 아래로 내려가지 마세요. 아직 아무도 들어보지 않았다고 솔직히 말하세요.
9. 모든 언어의 스틸 이미지, 그 다음 전체 렌더링
python render.py renders/check_en "lang=en×=25,66,241,273"
THREADS=6 CRF=17 python render.py renders/film_ru "lang=ru&fps=24&file=film_ru.mp4"
먼저 각 언어에서 스틸 이미지 검사를 통과하세요. 한 언어에서 잘못된 소수점 쉼표는 나중에 전체 재렌더링의 원인이 됩니다. 한 번에 하나의 무거운 작업만 실행하세요. 사운드만 변경하는 경우 그림 렌더링은 필요하지 않습니다: 믹스를 다시 만들고 finish_film.py를 다시 실행하세요.
10. 배포, 정리, 보고
python tools/finish_film.py ru
python tools/telegram_copy.py ru
python tools/chapters.py ru
플랫폼별 사본, 설명 및 보고서에 대해서는 references/delivery.md를 읽으세요. 사용자가 특정 게시물을 요청하지 않는 한, 사용자를 대신하여 업로드하거나 게시하지 마세요. 드래프트는 삭제하는 대신 휴지통으로 옮기고, 최종본과 이를 다시 만드는 데 필요한 모든 것을 보관하세요.
가장 많은 재작업을 줄이는 규칙
- 프레임은 오직
t에만 의존합니다. 시계, 시드되지 않은 난수, 이전 프레임의 상태는 없습니다. 그렇지 않으면 스틸 이미지가 영화와 일치하지 않고, 부분 재렌더링이 이웃 프레임과 일치하지 않습니다. - 시간은 큐(cue)에서 비롯됩니다. 큐로부터의 오프셋은 괜찮습니다. 입력된 절대 초는 다른 언어에서 깨지고, 재작성 후에도 깨집니다.
- 화면의 모든 텍스트는
T_를 거쳐야 합니다. 숫자도 포함하여.29,5 дня와29.5 days는 다른 문자열입니다. 이것이 실제 영화 영어 마스터에서 발생한 버그입니다:23,4°. - 언어 간 큐 순서를 가정하지 마세요. 단어 순서가 다르므로,
CUE.a < CUE.b는 그중 하나의 언어에서만 성립할 수 있습니다. 두 큐로 구성된 간격(span)을 보호하세요. - 라벨은 2D이며,
project()로 배치합니다. 그러면 평면 샷과 글자체, 그레인 및 언어 처리를 공유합니다. - 회전하는 물체 외부에 카메라를 유지하세요. 시트에서 이동의 극단값을 확인하고, 중간값만 확인하지 마세요.
- 정적 레이어는
plate()에 넣으세요. 모든 프레임에서 세부 배경을 다시 그리는 것은 렌더링을 느리게 만듭니다. - 변경한 후, 확인하고, 그 다음에 보고하세요. 프레임을 확인할 때까지 무언가가 수정되었다고 말하지 마세요.
확인할 수 없으며, 반드시 말해야 하는 것
- 사운드. 당신은 음량, 밸런스, 스펙트로그램 및 대본을 볼 수 있습니다. 소리가 어떻게 들리는지는 알 수 없습니다.
- 흐름(Flow). 당신은 프레임을 보지만, 시청자의 경험은 보지 못합니다. 따라서 페이싱(pacing)이 당신의 가장 약한 판단입니다. 특히 리듬에 대해 사용자에게 물어보세요.
- 플랫폼. 파일이 사용자의 Telegram 또는 YouTube 계정 내에서 어떻게 동작하는지 볼 수 없습니다.
무엇이 검증되었고 무엇이 검증되지 않았는지, 그대로 보고하세요. 결정(아이디어, 방향, 무엇을 삭제할지)은 사용자의 몫입니다.
번들 파일
| 경로 | 설명 | 읽어야 할 때 |
|---|---|---|
references/reference-analysis.md | 비디오 측정, 스타일 시트 템플릿을 위한 검증된 브라우저 스니펫 | 참고 비디오가 있을 때 |
references/script-and-voice.md | script.json, 큐 문법, 단어 예산, 음성, 사실 | 스크립트 작성 전 |
references/picture.md | 타임라인, 평면 키트, 큐 기반 연기, 텍스트, 그레인, 3D 키트, 예제 코드 맵 | 샷 작성 전 |
references/sound.md | 악기, 악보 구조, 음성 체인, 믹스 숫자, 검사 | 악보 작성 전 |
references/review-and-render.md | 시트에서 확인할 사항, 렌더링 모드 및 옵션, 부하 | 첫 리뷰 전, 전체 렌더링 전 |
references/delivery.md | 마스터, 전화기, Telegram, YouTube, X, 자막, 정리, 보고서 | 마지막에 |
references/pitfalls.md | 증상, 원인, 수정, 전체 파이프라인 | 무언가 잘못 보일 때 |
scripts/check_env.py | 머신이 가진 것 | 첫 번째 |
scripts/new_project.py | 템플릿에서 프로젝트 생성 | 두 번째 |
assets/template/ | 프로젝트 스켈레톤, 작동하는 데모 | new_project.py에 의해 복사됨 |
assets/examples/court-astronomer/ | 실제 영화 코드: 10개의 3D 빌더, 평면 샷, 핸드 리그, 전체 악보 | 특정 기법을 찾을 때; 단독으로 실행되지 않음 |