これはKLab Engineer Advent Calendar 2018の12日目の記事です。
12月1日~12月2日に秋葉原で開催されたTokyo Demo Fest 2018(以下、TDF)に参加しました。
TDFは、日本国内で唯一のデモパーティです。 コンピュータを用いて作成された楽曲や映像作品をデモと呼び、 デモに関心のある人々が一堂に会してコンペティションを行ったり、技術を共有したりといったイベントをデモパーティと呼びます。
今年のTDFでは、さだきちさん(@sadakkey)とチームを組み、『WORMHOLE』(映像:gam0022 / サウンド:sadakkey)という作品を発表しました。
Windows実行ファイル形式のデモ作品のコンペティションであるCombined Demo Compoにて、本作品が1位に選ばれました!
この記事では『WORMHOLE』の映像制作技術について解説します。 ソースコードを公開していますので、ご興味のある方はそちらもご確認いただければと思います(スターください!)。
サウンド編についてはさだきちさんが解説されています。あわせてご覧ください!
作品の概要
「ワームホールによる空間移動」をコンセプトとして、 不思議な球体がワームホールを介して非現実なデジタル空間と水平線の広がる自然空間を行き来する映像を制作しました。
不思議な球体がトンネルを進んでいくと、周囲を明滅する光がだんだんとモノクロからカラフルに変わっていきます。 トンネルの最奥にあるワームホールへ近づくほど明滅はだんだんと激しくなっていき、ホワイトアウトとともにワームホールを越えると、球体は海上に出現します。 その後、球体はじわじわと歪んでいき、戦闘機へと形を変えます。
変形中の不思議な球体の上には、私が尊敬するデモシーナーの名前を表示しました。 これはグリーティングと呼ばれるデモシーンにおける慣習です。
戦闘機はパーティクルを放ちながら海上を進み、パーティクルが一瞬だけTDFのロゴを形作ります。 そして戦闘機は元の球体に変形し、突如現れたワームホールに吸い込まれるようにして冒頭のトンネルのシーンに戻っていきます。
実装ならびに制作にはUnityを利用しました。 詳細は後述しますが、Timeline, TextMeshPro, Chinemachine, PostProcessingStack v2といったUnity 2018.2の新機能も活用しています。
レンダリング
映像の大部分はレイマーチングで描画し、パーティクルやグリーティングのテキストなどのレイマーチングが苦手とする部分はラスタライザで描画するというハイブリッドなレンダリング方式を採用しました。
なお、今回は制作期間が短かったため、レイマーチングのシェーディングにはUnity標準のディファードレンダリングを利用することにしました。 ディファードレンダリングにすることで、Gバッファの書き込みまでを実装すれば、それ以降のライティングの処理をUnityの標準のディファードレンダリングのシェーダーに任せることができます。 簡単に言ってしまえば、Unityでサポートされる全種類のライトやGI機能に対応するライティング処理をあえて自分で実装しなくて済むという、工数削減のメリットがあります。
Unityでディファードレンダリングによるレイマーチングを実現するにあたり、 @hecomiさんのuRaymarchingを利用させていただきました。 uRaymarchingは距離関数とGバッファに値を書き込む部分を実装すれば、簡単にレイマーチングができる便利なシェーダーテンプレートです。
他にも、鏡面反射による周囲の映り込みに、Unity標準のReflectionProbeを配置して実現しています。
uRaymarchingとReflectionProbeによる反射と組み合わせる検証
— がむ (@gam0022) 2018年6月3日
中
毎フレームCubemapを生成するくらいならレイトレで反射を計算したほうが速いと思っていたが、この例ならCubemapの解像度は16x16でも十分だし、Cubemapの方がポリゴンとの混在が容易なので、現実的な方法だと思う。 pic.twitter.com/sSX7WmVCEd
Full Screen Quadの実装方法
uRaymarchingの話に関連して、Full Screen Quadの実装方法について紹介します。
uRaymarchingではCommandBufferでフルスクリーンQuadを表示させていましたが、 スクリプトによる制御は最小限にしてEditorモードの挙動を安定させたかったので、別のアプローチをとってみました。
EditorツールでBoudingBoxを巨大にしてFrustum Cullingを無効にしたQuadを静的生成しました。
これによって時々レイマーチング部分が動かないトラブルを回避できました。 また、本作品のようにFull Screen Quadが必要なレイマーチングのワールドが複数存在して、 時間によって切り替わる表現のためには、MeshRendererのenableの切り替えで制御できる単純な仕組みの方が好都合でした。
Unityで画面全体にレイマーチングをさせる最高のソリューションができた!
— がむ (@gam0022) 2018年7月14日
CommandBufferを使う方法だとEditMode等の考慮が大変。
通常のQuadだとFrustum Cullingされて困る。
そこで、BoudingBoxを拡張したQuadを事前生成して通常のMeshRendererで描画できるようにした。https://t.co/Askoyvnq0X
トンネルのモデリング
トンネルはMenger spongeという有名なフラクタル図形をベースにしています。 回転のfoldのテクニックを利用して万華鏡のように見せたり、modをつかった図形の繰り返しのテクニックを適用しました。
回転のfoldは次の記事で紹介しています。
上の4種類の画像はいずれも同じ距離関数によるトンネルの様子です。 パラメータを変化させることで形状や色などを演出に合わせて変更できるようにしました。
海面のモデリング
海面は平面として衝突判定を行い、ノーマルマップだけで波が立っているように見せています。 こちらは以前にWebGLによって実装した『正解するカドの「カド」をレイマーチングでリアルタイム描画する | gam0022.net』と同じアプローチの軽量化方法です。
ところで、上記の記事の作品と異なり、本作品ではLODを一切行っておりません。 カメラワーク的に海面に近づかないため、そもそもLODが必要なかったのと、 マーチングループ中でテクスチャのフェッチをするとUnityのシェーダーのコンパイルが激重になる現象を回避するためです。
海面の質感は、Gバッファに書き込むパラメータの調整だけで再現しました。 ディファードレンダリングなので不透明オブジェクトとして当然ライティングされているのですが、どことなく海中を感じさせるような半透明の質感を擬似的に再現できたのではないかと思います。
戦闘機のモデリング
戦闘機は距離関数でモデリングしました。
3つのBoxの大きさをcos/sin/abs等で調整しつつ、smoothminによるメタボールでBoxを融合することで、流線形のSFっぽい戦闘機をモデリングしました。
また、フラグメントシェーダーの負荷軽減のためにObject Space Raymarchingを行いました。 Full Screen Quadを使わずに戦闘機と同じ大きさのSphereを配置し、Sphereのシェーダーでレイマーチングをしています。 上記の画像を拡大するとSphereのワイヤーフレームを確認できます。
演出の実装
TextMeshProによるフォントのレンダリング
フォントはプロシージャルではなくテクスチャを使用しています。 TextMeshProのEditorツールを利用して Azonix fontのデータからSDFのフォントのアトラステクスチャを生成しました。
生成したアトラステクスチャはTextMeshProのシェーダーでレンダリングしています。
次のような簡単な文字の出現と消滅のエフェクトを、TextMeshProの標準シェーダーの一部を改造して実装しました。 この演出に関する解説をUnity #2 Advent Calendar 2018の19日目の記事で行いました。
TextMeshPro シェーダー遊び その3#unity3d #Unity #creativecoding pic.twitter.com/bUJvfyDhBr
— がむ (@gam0022) 2018年10月28日
Animation Track vs Custom Track
UnityのTimelineではトラックを自作することができます(以降、自作トラックのことをCustom Trackと書きます)。
Custom Trackの実装はそれなりに工数がかかります。 たとえば、クリップのパラメータを1つでも増やすと複数箇所に変更が発生します。 工数が限られている場合や試行錯誤しながら色々なパータンを作る場合には、Animation Trackでは実現できないのかを事前に確認することをおすすめします。
本作品でも、基本的にはAnimation Trackを利用し、アニメーションでは制御できないTextMeshProの文字列指定においてのみCustom Trackを利用する方針としました。
パーティクル
パーティクルはUnityのParticleSystemを利用しました。
次の画像はポストエフェクトとSkyboxをOFFにした状態でパーティクルをワイヤーフレーム表示したものです。
パーティクルの形状は5種類でしたが、パーティクル用のモデルは1種類しか用意しませんでした。 四角形のQuadをフラグメントシェーダーでdiscardして形状を変化させました。 すべてのパーティクルを1マテリアルで表現できるので、全パーティクルを1ドローコールで描画できました。
4種類のパーティクルが当時に登場する演出では、Custom Vertex Streamsを用いてランダム値をシェーダーに渡し、シェーダーで形状の切り替えを行いました。
ワームホールの実装
「ワームホールの中身だけ別の世界になる」演出にも戦闘機と同じObject Space Raymarchingの仕組みを利用しました。
まずHoudiniでワームホールの八角形のポリゴンメッシュを作成しました。
この八角形のメッシュのシェーダーでObject Space Raymarchingを行えば、別の世界と繋がる演出ができます。
ところが、カメラの原点からレイを進めると、別世界が3Dの立体映像のように飛び出してしまうという罠にハマってしまいました。 この問題はレイを物体の表面から進めることで回避できました。
ワームホールの内側は現在の世界(上の画像では海の世界)のレイマーチングのシェーダーを無効にしたかったので、Stencilを利用しようとしたのですが、 UnityのディファードレンダリングではStencilの利用が制限されていました。
ShaderLab: ステンシル - Unity マニュアル
deferred レンダリングパスでレンダリングするオブジェクトのためのステンシル機能はいくらか制限されます。それらの 2 つのステージの間、シェーダーで定義されるステンシルステートは無視され、最終的なパスの間に考慮されるだけです。
そこで、DepthテストとRenderQueueによる制御でStencilを代用しました。
ReflectionProbeの映り込みによる演出
2回目のワームホール出現時(2:05〜)に海面が黒く侵食されていく演出があります。
これは、ワームホールの向こう側の景色がReflectionProbeに映り込み、Unityのライティング機能によって自動的に水面に反映された結果です。 意図的に演出したものではなく偶然の産物でしたが、気に入ったのでこのまま採用しました。
揺らぎ
揺らぎは2箇所で利用しました。 単純でコストもかからない工夫ですが、効果は大きいと感じました。
カメラにfbmノイズを加えて手ブレ感を出すことで臨場感が生まれました。
それから、戦闘機をcos波で振り子のように左右に揺らしています。 戦闘機の動き自体はZ軸に直進するだけなのですが、機体の揺れとカメラワークによって旋回しているような雰囲気が出ているのではないでしょうか。
音楽との同期方法
ビート単位でのシェーダー制御
シェーダーの入力をビートにし、演出を「ビート単位」で制御することで、映像と音楽を同期させました。 時間単位(秒単位)で制御するよりも、BPM変更に柔軟に対応できるというメリットがあります。
秒数 time
を特定のBPM bpm
のビートに変換するには beat = time * bpm / 60
を計算します。
カメラのカット切り替えやパーティクルの同期
カメラのカットやパーティクルのエミットのタイミングといったシェーダーで制御していない部分は、 音楽に合わせてTimelineのクリップを手動で配置する必要がありました。
こちらは音楽を120BPMで制作していただいたことで、かなり楽に解決できました。
120BPMでは、1ビートが0.5秒となります。
4分の4拍子であれば1小節の長さが2秒となるため、カメラのカット切り替えを2秒単位にすると音楽と映像が自然に同期します。 同様に、4分の3拍子であればカット切り替えを1.5秒単位にすればよいわけです。
パーティクルは、エミット間隔を0.5秒ごとに設定することで音楽とタイミングを合わせています。
来年の抱負
次はライティングに凝ってみたいです。
物理ベースレンダリング(PBR)で攻めるのであれば今回のライティングはUnityに任せる作戦で正解だと思いますが、 非現実的なレンダリング(NPR)には対応できないので、ディファードレンダリングのライティングパスの独自実装などを調査したいです。
他にも、Unityの新機能のScriptable Render Pipeline (SRP) や High Definition Render Pipeline(HDRP)とレイマーチングを組み合わせる検証などもしてみたいです。
おわりに
『WORMHOLE』の映像を作るための取り組みや手法について技術的な視点で解説しました。
上記の通り、『WORMHOLE』の制作にはUnityの機能やライブラリを多く利用しています。 巨人の肩の上に立つことで表現の部分に注力でき、3週間弱という短い制作期間の中で完成度の高い作品に仕上げることができました。
とはいえ、制作期間中は『WORMHOLE』を受け入れてもらえないのではと常に不安を感じていました。 デモシーンの世界ではゲームエンジンの機能に頼らない高度な実装力こそ評価されると思っていたからです。 そんな予想に反し、Unityで作成したデモ作品を高く評価していただけて大変光栄です。
Unityには初心者~上級者まで様々なレベルの方を対象とした資料や教材があります。 『WORMHOLE』では使用しませんでしたが、たくさんのアセットも用意されています。 デモシーンに興味はあるもののハードルが高そうで踏みとどまっている方や、レンダリング技術の学習に挫折してしまった方に、Unityでもデモ作品を作成できることをお伝えしたいです。 また、日頃の業務でUnityを利用している方に、自分でも作れそうな身近なものとしてデモシーンに興味を持ってもらえれば嬉しいです。 『WORMHOLE』が新たなデモシーナーを生み出すきっかけとなれば幸いです。
最後に、素晴らしいサウンドを生み出してくれたさだきちさんに感謝申し上げます。 チームでTDFに参加するのは今回が初めてでしたが、非常に良い経験をさせてもらいました。 自分の映像にかっこいい音楽が組み合わさった時の喜びや興奮は忘れられません!ありがとうございました!!
関連情報
『WORMHOLE』を高画質で見るには
下記の実行ファイルか動画ファイルをダウンロードしていただくと、エンコード前の綺麗な画質でご覧いただけます。
- Windowsの実行ファイル(GTX1070以上のGPU推奨)
- 動画ファイル(ブラウザ上だとエンコードされた状態で再生されるのでダウンロードしてください)
『WORMHOLE』の感想をお待ちしております!
pouet.netという世界中のデモ情報を集めたポータルサイトに作品を公開しました。
作品の感想をYouTubeやpouet.netでいただけると泣いて喜びます。