8kBで制作したアニメーション映画
(ctrl-alt-test.fr)- Ctrl-Alt-Testチームは2022年11月から、短編アニメーションのように見えるリアルタイムデモを8kBに収める実験を始め、2023年4月にThe Sheep and the Flowerを公開した
- 成果物はリソースファイルや外部依存なしの単一
.exeがグラフィックスと音楽を生成し、シーンはGLSLシェーダーとレイマーチングによってGPU上でリアルタイム計算される - 羊、標識、カメラ、タイムラインは保存されたモデルやテクスチャではなく、SDF、手続き的3Dテクスチャ、ハードコードされた時間パラメータで構成され、約25個のカメラショットが手作業で調整された
- 音楽はOpenMPTと4klangで制作され、楽器数を16個から13個に減らし、反復を増やして圧縮効率を高めた
- 最終的な8kBはCrinkler圧縮とShader Minifierの改良によって実現し、42kBのシェーダーコードは縮小と圧縮後に約5kBに収まり、音楽とC++コードのための空間を残した
8kBに収められた単一実行ファイル
- The Sheep and the Flowerは、2023年4月に公開されたWindows実行ファイル形式のリアルタイムアニメーション
- 1つの
.exeファイルがすべてを生成し、別途リソースファイルや外部依存はない- 実行にはWindowsと最新ドライバーが必要
- 目標は8kBの中にグラフィックス、アニメーション、演出、カメラワーク、音楽をすべて入れることだった
- ソースコードはGitHubで公開されている: ctrl-alt-test/mouton
グラフィックス・音楽・圧縮に使われたツール
- すべての視覚要素はGPU上でリアルタイム計算される
- GLSLシェーダーにはタイムライン情報、カメラ設定、シーン表現まで含まれている
- レンダリングはレイマーチング(raymarching)で行われる
- シェーダーはShader Minifierで縮小される
- 音楽はOpenMPTと4klangシンセサイザーで制作された
- 楽器は手続き的に記述される
- 音符リストは圧縮される
- コードはVisual Studio 2022でC++により書かれた
- コンパイラフラグと初期化はLeviathanフレームワークをベースに始めた
- 最終実行ファイルはCrinklerで圧縮された
なぜ8kBだったのか
- デモシーンには4kBや64kBのようなサイズ制限カテゴリが一般的にある
- 既存の作品は主に64kBを目標にしていたが、8kBはまったく異なる制約とルールを提供する
- 4kBイントロの技法は興味深いが、まともなストーリーテリングには制約が大きすぎると考えた
- Revision demopartyが数年前に8kB競技部門を追加し、それを実験の良い機会とした
2つの三角形で描く世界
- デモシーンで2008年以降標準的に使われてきた方法のように、画面全体を覆う四角形を2つの三角形で描く
- この四角形の上でGLSLシェーダーが各ピクセル、各フレームの色を計算する
- シェーダー関数は座標と時間を入力として受け取り、色を返す
- 中核となる課題は、この関数の中で羊とシーン全体をどう表現するかだった
SDFでシーンを構成する
- 符号付き距離場(SDF) は、空間内のある点から最も近い物体までの距離を計算する関数
- 表面上では0を返す
- 物体内部では負の値を返す
- 球や箱のような基本図形は簡単な数式で表現できる
- 複数の距離場を組み合わせてシーンを構成する
- 2つの物体の和集合は距離の最小値で作れる
- 交差は最大値で作れる
- スムーズな和集合(smooth union)を使うと、より有機的な形を作れる
- 羊は円錐や球のような単純図形を組み立てて結合して作られ、胴体と毛は3Dノイズ関数で構成された
レイマーチングでピクセルを決める仕組み
- レイマーチングは、SDFを用いて3Dシーン内で光線を少しずつ前進させるレンダリング技法
- 従来のレイトレーシングのように交点を数学的に直接計算しない
- SDFが衝突せずに進める距離を教え、その分だけ光線を前進させる処理を繰り返す
- 距離が0または0に近くなれば交点を見つけたとみなす
- 交点が生じると、現在のピクセルが羊、空、他の物体のどれに当たるかが分かる
- ライティングには表面法線が必要で、周囲の勾配を計算して法線を推定する
- 影は交点から太陽方向へ別の光線を飛ばし、途中に物体があるかを確認して計算する
小さな表現力で物語を伝える方法
- デモには音声も文字もなく、キャラクターも1体だけ
- 羊は歩くこと、頭と目を動かすことくらいしかできず、回転もできない
- この制約の中で感情を伝えるため、物語に関係のない要素を取り除いた
- 当初は砂漠や砂丘、空を考えていたが、物語には不要だと判断した
- 背景は真っ白のままにした
- 意味を伝える標識と目を除いてテクスチャを減らした
- 範囲を小さく保つことで、ディテール、磨き込み、カメラワーク、編集、音楽の同期に集中した
- 各ショットは手作業で構成され、アニメーションは流れが自然に感じられるよう何度も反復調整された
カメラと演出
- カメラは物語を伝える道具として使われる
- 羊が長い時間さまよう場面では広いショットを使って孤独感を作る
- 標識に気づく瞬間には目のエクストリームクローズアップを使う
- 羊が標識を見るときは頭の方へゆっくりズームインして集中感を高める
- 羊の興奮は複数の表現を重ねて伝える
- 漫画的な2D効果
- 速くなった足取り
- 強調されたしっぽの動き
- 誇張された頭の振り
- 音楽の劇的な変化
高速な反復のための開発手法
- ソースコードには多くのハードコードされた定数が入っている
- 羊の目の大きさ
- カメラ移動速度
- 各ショットの長さ
- 花の色のような値が繰り返し調整された
- 迅速なフィードバックのため、シェーダーをランタイムで再コンパイルして1秒以内にグラフィックスを更新した
- アニメーションとカメラ調整のためにプレイヤーが必要だった
- 一時停止
- 再生
- 時間制御
- シェーダーのライブリロード後に即確認
- 音楽とアニメーションを正確に同期するには音楽サポートも必須だった
- 初期プロトタイプはShadertoyで作り、その後KodeLifeを経てC++プロジェクトへ移した
4klangで作った音楽
- 音楽は物語を伝えるうえで重要な要素であり、複数の雰囲気や特定のタイミングでの切り替えが必要だった
- 4kBイントロで使うツールをそのまま使いつつ、より複雑な構成のためにより多くの空間を割り当てた
- 4klang synthesizerは音楽ソフトウェアで使えるプラグインを提供し、アセンブリファイルを出力する
- このアセンブリファイルはデモと一緒にコンパイル・リンクされる
- デモ実行時にはシンセが別スレッドで波形音を手続き的に生成し、サウンドカードへ送る
- 最初に作った音楽は予想より大きく、サイズを減らすために複数の調整を行った
- 楽器数を16個から13個に減らした
- エンディングテーマを全体のテンポに合わせて書き直した
- 反復を増やして圧縮しやすい構成にした
- 背景音の音符長のように耳ではほとんど分からない変更で圧縮率を改善した
- 音楽全体の構造を保ちながら品質低下を最小限に抑え、容量を節約した
アニメーションと時間同期
- デモは事前計算やキャッシュなしで、毎フレームすべてを再評価する
- 性能面では不利だが、あらゆる要素を時間依存で変化させられるため、アニメーションには有利だった
- デモ全体は約25個の手作業カメラショットで構成される
- 各ショットは18個のパラメータが時間に応じてどう変化するかを記述する
- 各物体の位置
- 羊の状態
- カメラ位置
- 焦点
- カメラが見る対象など
- 例として
camPos = vec3(22., 2., time*0.6-10.);という1行だけで、ショット中のカメラの線形移動を表現する timeはデモ開始以降の絶対時間ではなく、ショット開始以降の時間を意味する- ショットの挿入、削除、調整をしてもデモ全体への影響を減らせる
- 単純な線形補間は機械的に見えることがあるため、複数のケースでsmoothstepを使用した
- タイムラインコードはvertex shader内にある
- ショット定義には繰り返しコードが多いが、その反復は圧縮に有利に働く
テクスチャと材質
- 従来のレンダラーでは2Dテクスチャを3Dモデル表面に貼る
- レイマーチング方式ではテクスチャ座標を簡単に計算しにくいため、代わりに3Dテクスチャをその場で計算する
- レイマーチャーがレンダリング対象の3D位置を見つけたら、その3D座標をテクスチャ関数に渡す
- 交通標識は数式で三角形や四角形の枠を作り、内部コンテンツは複数の関数を組み合わせて描く
- レストラン記号は黒い楕円形4つと、くぼみを作る白い形2つで構成される
- 視覚的な多様性のため、テクスチャだけでなく材質も変えて設定した
- 例えば羊のひづめはフレネル係数を使って異なる反射を持たせた
目に命を吹き込む偽の反射
- 開発中、長い間目が生気のない平坦な見た目になっていた
- 目はキャラクターデザインと物語伝達の重要な要素だった
- 漫画キャラクターの参考画像では虹彩が多いが必須ではなく、瞳孔は常に大きく描かれる傾向があることを確認した
- きらめく目を作るには、目の中に光の反射が必要だった
- 既存のライティング方程式では、太陽とカメラが特定位置にあるときだけ反射が生じた
- これを補うため、表面法線ベクトルを修正して太陽反射が起きる確率を高めた
- さらに環境マッピングを使った
- 通常はゲームで実際の環境反射を単純化して最適化するために使われる
- ここでは実世界がほぼ真っ白だが、テクスチャでディテールを偽装的に追加した
- 白目と瞳孔には、空っぽの世界よりはるかに複雑な反射が入っている
- 複数の偽の光源
- 暗い地面と青い空を模したグラデーション
ポストプロセスとビジュアルスタイル
- 最終的な雰囲気と画質はポストプロセスで仕上げた
- 使用したポストプロセスは以下の通り
- カラーグレーディング
- ガンマ補正
- 弱いビネット
- エイリアシングを減らすための2パスFXAAフィルター
- 目の星エフェクトやエンディング画面の効果のような一部の効果もポストプロセス段階で実装された
- これらの効果は純粋な2Dであり、3D世界には存在しない
- 一時は古い漫画風スタイルも試した
- 手描き風に見せる輪郭線検出
- 白黒レンダリング
- グレインとノイズ
- 議論の末、この実験はやめて、よりクリーンで現代的な外観に集中した
Crinkler圧縮
- 中核となるアイデアはデータを保存するのではなく、データを生成するコードを保存するというもの
- 音楽は演奏する音符リストと各楽器の命令リストを保存する
- Crinklerはデモシーンと1kB〜8kBイントロ向けに設計された圧縮ツール
- 実行ファイルは自己展開可能である必要があるため、Crinklerは残りの実行ファイルを展開できる小さなアセンブリコードを含む
- Crinklerはサイズ最適化を最優先する
- 圧縮アルゴリズムには時間がかかる
- 展開は比較的遅い
- 数百MBのRAMを使う
- しかしCrinklerだけでは十分ではなく、42kBのシェーダーソースコードを実行ファイル内に入れるため追加の縮小が必要だった
Shader Minifierの改良
- シェーダーのソースコードは最終バイナリに含まれるため、可能な限り小さくする必要がある
- 手作業で縮小することもできるが、保守性と高速な反復に問題が生じる
- Shader Minifierは2011年から開発されているシェーダー縮小ツール
- 空白やコメントを削除し、変数名を短くし、さらに多くの変換を行う
- 8kBイントロは4kBよりはるかに多くのコードを含むため新しい問題が生じ、必要な機能を追加するために1か月間デモ制作を止めた
- 良い縮小のためには単純なミニファイアではなく、Closure Compilerに近いソースからソースへのコンパイラが必要だった
- Shader Minifierが行う変換には以下が含まれる
- 変数名と関数名の変更
- 変数のインライン化
- 定数算術式の評価
- 関数のインライン化
- デッドコード除去
- 宣言のマージ
- コードそのもののサイズを減らすだけでは十分でなく、圧縮しやすい出力が必要だった
- ある変換はコードサイズを減らしても、圧縮後サイズを増やすことがある
- デモを反復開発する間、圧縮後サイズを継続的に確認する必要があった
- 新しいShader Minifier改良によって、圧縮バイナリで約600バイトを節約した
- 最終的に42kBのシェーダーコードは縮小と圧縮後に約5kBへ収まった
- 残った空間には音楽とC++コードが入った
他者の仕事の上に積み上がった成果
- このデモは、レイマーチング技法、音楽生成ソフトウェア、圧縮アルゴリズムなど、他の人々が作った仕事の上に成り立っている
- Shader Minifierに追加された新機能が、今後より良いデモ制作に役立つことを期待している
- 8kBカテゴリは4kBより多くの可能性を提供しており、より人気を集めてほしいという期待が残っている
- 比較のため、原文テキストは約21,000字あり、そのままなら21kB必要になる
1件のコメント
Hacker Newsのコメント
この映画がインスピレーションを受けた元映画の制作者です: https://www.youtube.com/watch?v=khWXdkryBE4
元作品はBlender、SVG、音声ファイルだけでもMB単位なのに、リメイクが8kBに収まっているのは本当に印象的です
Creative Commonsライセンスが実際に役立ったのはうれしい。ただ、同じハリネズミのキャラクターと音声メロディを使ってくれていたら、なお良かったと思います
Slootが復活した!!!
https://en.m.wikipedia.org/wiki/Sloot_Digital_Coding_System
デモシーナーを大勢集め、映画を見せ、コンピューターのある部屋にしばらく閉じ込める
大画面、大きなサウンドシステム、ビール、ジャンクフードがあるとさらによく機能し、あとは成果物の実行ファイルを受け取ればいい
a性能のためRustで書いたインタプリタが
aコマンドを評価し、ランタイムに含まれる特定のペイロードをSTDOUTへ出力するみんなにとって本当にひどい不運だった
その人たちは a) 一方向関数であること、b) たとえ一方向でなくても複数の入力が同じハッシュにマップされ得ること、を理解していなかったのだろう
デモシーンの動画はかなり面白いのに、ばかげたほど知られていない
https://www.youtube.com/playlist?list=PLRQwR4-_0PR9HBI_GZs1nTn7q5LTkGeCN
retro game mechanicsのように、良いビジュアル素材で内部を解説してくれるチャンネルがあるといいのに
動画がなかった頃は、解凍と処理をかなり待ってからでないと、感嘆することも失望することもできなかった
昔は休憩時間にデモをたくさん見ていて、当時の精神状態には完璧な背景だった
https://en.wikipedia.org/wiki/.kkrieger
さらに進んで、互いに知的なエージェントで構成された世界をどれだけ小さく作れるのだろう?
追記: 低評価している人たち、夢を見ないのか? これから本当に狂ったように幻想的なものがたくさん待っている
背後の技術はかなり違うのは分かっているが、昔の**.kkrieger**を思い出す。3D FPS全体を96kに圧縮していて、本当に見事だった
https://en.m.wikipedia.org/wiki/.kkrieger
デモシーンを多く見て制作手法を読んでいると限界が分かってきて、羊のような要素、特に脚の円錐断面は非常に圧縮された方程式で表現し、同様にアニメーション化できることが明らかになる
ただ、私の知る限りほとんどのデモはGPUと強力な3Dアクセラレーション能力に依存している一方で、2D日本アニメ風のデモは小容量では珍しいか、ほとんど存在しないように見える
実際には3Dアニメーションの方が簡単なのだろうか?
ちなみに「mouton」はフランス語で「羊」で、だから「mutton」なのだ
一方、2Dスタイルのアニメーションを保存する「数学的」な方法は、輪郭線をSVG曲線として保存し、領域の塗りには空間充填アルゴリズムを使う程度だ
ざっくり計算しても、曲線の単一の弧や線一本が、幾何学的な体積全体の記述と同じくらいの保存領域を食う可能性があり、それらの曲線のアニメーションまで保存しようとすると、3D体積の小さな変換行列に比べてはるかに多くの領域が必要になる
レンダリングアルゴリズムの複雑さも、実際のアルゴリズム複雑性と空間複雑性の両方で増えると思う
ただし、複数のビットマップを保存してフリップブックのように再生するのも、差分からフルフレームを生成する手続き型の解凍アルゴリズムを使うなら「数学的」だと主張はできる
しかしそのような方式が存在するとは思わないし、3D体積の記述に比べると空間要件は途方もないものになるだろう
8kBよりは少し大きいが、羊に関する意外にダークなアニメーションが好きなら、Blender FoundationのオープンムービーCosmos Laundromatをおすすめする
この財団の映画は技術デモにしてはいつも妙に暗いが、この作品は特によくできていたと思う
[0] https://www.youtube.com/watch?v=Y-rmzh0PI3c
あらかじめ警告しておくと、この映画は「to be continued」で終わり、Wikipedia のページにはこう書かれている:
「この映画はもともと、長編映画の出発点として意図されていた。短編の続編には脚本とデザインがあったが、制作には入れなかった。2020年に[制作者]は、この1本がプロジェクトのすべてになると発表した」
興味深いコンセプトの結末を永遠に見られないという失望を味わいたくないなら、飛ばしたほうがいいかもしれない
DaVinci Resolve で HDR 実験をする時に役立った
[1] https://opencontent.netflix.com/#h.uyzoa2bivz2j
8KB はデモシーンの一部門。Pouet にはこうした作品をよく整理したインデックスがある
0. https://www.pouet.net/prodlist.php?type%5B%5D=8k&page=1
Razor 1911。純粋なノスタルジー
何十年も前の良い思い出がたくさんよみがえる :-)
本当に驚くべきもので、制約のある状況でも物語がこんなに笑えて創造的である点が好き。実に印象的
バイト数を減らすためにどのような トリックや節約 をしたのか、技術的な詳細をもっと聞きたい
このコミット(https://github.com/ctrl-alt-test/mouton/commit/79d2d1eab7a222b434b7041d8fc9bce4e60524f8)では、性能最適化を削除して多くのバイトを節約している
本来は残したかったがバイトが足りず、比較的新しい GPU ではその最適化が不要だと分かった
笑えるのに完全に驚かされる。ついに 結末を予想できない映画 を見た