Search Unity

ADAM: The Mirror』そして『ADAM: Episode 3 』は、もうご覧になりましたか?何百万人もの心を掴んだこの二つの短編映像作品ですが、多くの人が興味津々なのは、ニール・ブロムカンプ氏とそのチームが一体どのようにして、リアルタイムであの素晴らしいエフェクトを実現させたかということです。本ブログでは、制作の舞台裏を掘り下げた、ライティングAlembic への対応衣服のシミュレーションタイムライン、 シェーダー、リアルタイムレンダリングなどに関する記事も公開していますので、是非併せてお読みください。

John Parsaie は Made with Unity チームのソフトウェアエンジニアです。最近公開された映像作品 ADAM ではサブサーフェス・スキャタリング(表面下散乱)や透明なポストプロセッシングエフェクト、Alembic グラフィックの統合などを担当しました。このチームに加わる前は、バーモントとモントリオールで勉強しながら、 Unity と Warner Bros. Interactive でインターンのエンジニアとして働いていました。

Yibing Jiang は Made With Unity チームのテクニカル・アート・スーパーバイザーです。 Unity に来る前は、アニメーション映画のトップスタジオとAAA ゲームのトップスタジオの両方で仕事をし、これまでに『アンチャーテッド 4 』(ノーティードッグ)のキャラクターシェーディング、『モンスターズ・ユニバーシティ』 および『カーズ 2』(ピクサー)のセットのシェーディング、『シュガー・ラッシュ』(ディズニー) のルック・デベロップメントなどを手掛けてきました。

はじめに(フレームの分析)

息をのむような ADAM のリアルタイム映像 ― そこには、多数の要素が Unity によって統合されて生み出された数々のエフェクトが盛り込まれ、多くの注目を集めました。本記事では、Oats Studios がどのようにしてあの印象的なエフェクトを実現したかを、二つの重要な側面に的を絞って見ていきます。上記で紹介したアーティスト達の使用した Unity 2017.1 のカスタムシェーダーの詳細や、特定の 1 フレームに焦点を当てたリアルタイムレンダリングの分析に興味のある方は是非この先を読み進めてください。

本記事で分析する『ADAM: Episode 3』のフレーム

本記事では、『ADAM: Episode 3』から上図の画像のフレームを、 RenderDoc (フレーム分析とグラフィックのデバッグのための大変便利なツール)を使って分析していきます。この分析によって、Oats Studios が実現した視覚表現を一歩踏み込んで理解することができるでしょう。すでに RenderDoc と Unity のエディターとの統合機能がビルトインで提供されていますので、ご自身のプロジェクトで同じような分析を行いたい場合にも、Unity の フレームデバッガー の次のステップとして便利に使用できます。 Unity と RenderDoc の併用についての詳細はこちらでお読みいただけます。

G バッファのレンダリング

ADAM の映像は両方とも Unity 2017.1 の遅延レンダリングパスでレンダーされています。つまり、すべての不透明なオブジェクト*が、G バッファ(ジオメトリバッファ)として集合的に参照される一式のバッファ内にレンダーされているということです。G バッファには、レンダーパイプラインで後々実行されるライティング計算に必要となるデータが、全て含まれています。

G バッファ(ジオメトリバッファ)とアルファチャンネル(左下隅)

マルチプルレンダーターゲット(MRT)をセットアップすると、同じドローコール内で G バッファの 4 つの構成要素すべてに、それぞれの不透明なオブジェクトに関する次のデータを書くことが可能になります。

1. 拡散色の RGB/オクルージョン・アルファチャンネル(ARGB32)

物体の「固有の色」および、ジオメトリのベイク済アンビエント・オクルージョンです。

2. 反射色の RGB/スムースネス・アルファチャンネル(ARGB32)

Unity はスペキュラーおよびメタリック両方の PBR(物理ベースレンダリング)ワークフローに対応しています。ただし内部的にはどちらのワークフロー入力も、このバッファ内に書かれた同じ情報に落とし込まれます。これは、後に使用されるひとつのシェーディングモデル下で PBR 入力を一元化するために行われます。

3. ワールド法線のRGB/アルファチャンネル未使用(ARGB2101010)

より精度の高いバッファは、ワールド空間法線や、面の向いている方向を各ピクセルで保存するために使用されます。この情報は、光源との関係に基づいて照度を計算する上で極めて重要です。

4. 放射光、環境光のRGB/アルファチャンネル未使用(ARGBHalf)

反射光とアンビエント GI はバッファ内にレンダーされます。このバッファは、パイプラインの後段で反射の収集とライティングの集積にも使用されます。このバッファはユーザー設定によって LDR 形式 または HDR 形式になっています。 ADAM は HDR ライティングでレンダーされています。

*透明および不透明なアイテムでカスタムのシェーディングモデルを持つものは、パイプラインの後段で異なる形で扱われます。

デプスバッファとステンシルバッファ

G バッファがレンダーされると、シーンの深度もそれ自体の特殊なバッファ内にレンダーされます。深度の保存は、リアルタイムグラフィックにおいては、2D 空間へのシーンの投影中・投影後の両方で、三次元の感覚を維持するために非常に重要です。またこれは、後々遅延シェーディングで必要になる、ピクセルのワールド位置を再構成する上においても不可欠です。さらに、リアルタイムでよく使われる高度な後処理エフェクトの根幹となるのは、このデプスバッファとステンシルバッファです。

デプスバッファとステンシルバッファ

ステンシルバッファ(右)はデプスバッファと同じリソースを使用しています。ステンシルバッファは、ピクセルをピクセル自身にレンダーされた要素に基づいて分類する際に特に役立ちます。後々この情報を利用してピクセルを識別し、各ピクセルに何の処理を施すか選択することができます。Unity では、ステンシルバッファはライトカリングマスクに使用されています。 ADAM では、サブサーフェス・スキャタリング(SSS)を起こすオブジェクトを判別するためにも使用されています。

サブサーフェス・プロファイル・バッファ

サブサーフェス・スキャタリング(表面下散乱)については、レンダラーが拡張され、(G バッファの生成中に)追加のバッファ内にインデックスも書き込まれるようになりました。このバッファは後に、サブサーフェス・プロファイルから送られた重要なデータを含むバケット内の検索に使用されます。またこのバッファには、どの程度の散乱が起こるべきかを示すスカラーも保存されています。

【サブサーフェス・プロファイル・バッファ】( R )プロファイルインデックス、 ( G )散乱範囲(半径)

上述の通り、重要なデータがサブサーフェス拡散プロファイルから取得されますが、このプロファイルはエディター側でユーザーが作成します。このユーザー定義のプロファイルによって、透明性の高い媒体中で拡散光がどのように散乱するかが設定されます。

サブサーフェス・スキャタリング・プロファイル

前方散乱や透過率もこれらのプロファイルによって制御可能です。この例は、肉の薄い耳や鼻孔を光が通過するショットなどで見ることができます。これらの情報は全て GPU に送られ、後で読み出されます。

次のステップ

G バッファがレンダーされた時点で、シーン ジオメトリの複雑な要素がすべて効率的に、少数のバッファ上へと遅延されたことになります。これにより、後の処理にかかるほぼすべての計算コストが、ある程度一定した予測可能なものになります。これはなぜかと言うと、ライティングの計算が、シーンのジオメトリの複雑性に全く影響されなくなったからです。しかし、主要なライティングの前に、事前に必要な重要なパスがいくつか残っています。以下でこれらを見ていきましょう。

環境反射

最近作成された G バッファ からのデータを使用して、スカイボックス キューブマップに照らし合わせて計算が実行され、環境反射が求められます。ラフネス・法線・ビュー方向・反射色などの情報を踏まえて一連の方程式を解くことで、物理的に正しい環境からの反射が得られます。こうした反射は、発光 HDR バッファに加算ブレンドされます。

環境反射

シャドウ

これで、レンダラーが必要とする事前処理のほとんどが完了しました。ここでレンダラーは遅延ライティングの段階に入ります。これはシャドウから始まります。

Unity はディレクショナルライトに、カスケードシャドウマッピング( CSM )という広く知られたテクニックを使用しています。このコンセプトは単純です。我々の眼は遠くを見れば見るほどディテールが見えなくなる。だとすればコンピューターグラフィックでも、遠方の物のディテールの計算に多くの資源を費やす必要はないだろう、という考え方です。CSM は、このコンセプトに基づいてカメラからの距離に応じてシャドウマップの解像度を変化させる技術です。

(左)カスケードシャドウマップ(CSM)、 (右)スポットライトシャドウマップ

このショットでは、実はディレクショナルライト CSM は環境ジオメトリにのみ使用されており、 2 人のキャラクターに関しては一式のスポットライトが使われています。同じことが、これ以外のいくつかのショットでも行われています。 Oats Studios のライティングアーティストが何故このようなやり方を選んだかと言うと、ショット内の重要なビジュアル要素を強調する上で、より自由度の高い方法だったからです。

スクリーンスペースシャドウ

「スクリーンスペースシャドウ」あるいは 「コンタクトシャドウ」と呼ばれる技術も導入されています。これはデプスバッファをレイマーチングすることで精度の高いシャドウを実現するものです。この技術は、時に CSM でさえ表現し切れなかった、Oats の写真測量環境における粒度の細かいシャドウのディテールを表現可能にした、とりわけ重要な技術です。スクリーンスペースシャドウは、漏れる光を「埋める」 Unity のシャドウマッピングのテクニックと併用できます。

遅延シェーディング

これですべてのピースが揃い、各ピクセルのライティング状況を完全に再現するのに十分な情報が整いました。

遅延ライティングパスへの入力情報一覧

遅延ライティングパスは、視界内のそれぞれのライト毎に 1 回パスを実行し、1 回のパス毎に HDR バッファにライトを集積します。 G バッファの内容は、シャドウマップを含む現在のライトの情報を使用して計算されます。

最初のステップとして、ピクセルのワールド空間ポジションがデプスバッファから再構成されます。ピクセルのワールド空間ポジションは後に、面上の地点の、眼に対する方向を計算するのに使用されます。これは後々、視点に基づくスペキュラー反射を決定する上で非常に重要です。シャドウその他のライトの情報(クッキーなど)も、最終結果の減衰のために単一のスカラー項に集められます。次に、G バッファからすべての面データが取得されます。最後にすべてが、Unity のシェーディングモデルである物理ベースの微細表面双方向反射率分布関数(BRDF)にサブミットされ、最終的なシェーディングが行われます。

サブサーフェスオブジェクト以外の不透明なオブジェクトの最終的なライティングをすべて集積

ここまでの時点でシーンのシェーディングはほぼ完全に施されましたが、白いアウトラインは一体何でしょう?これは上記で触れた、ステンシルバッファ内でサブサーフェス・スキャタリング用に印を付けたピクセルで、これらのピクセルのシェーディングはまだ行われていない状態なのです。

サブサーフェス・スキャタリング(表面下散乱)

上記で少し触れましたが、サブサーフェス・スキャタリングとは拡散光の散乱および再出現であり、これは光を通す媒体中において最もよく見ることができます(実はサブサーフェス・スキャタリングは金属以外のすべての物体中においてある程度は起こるものなのですが、大抵の場合はこれに気付かないのです)。皮膚などはこの最も典型的な例です。

しかし、リアルタイムコンピューターグラフィックスにおけるサブサーフェス・スキャタリングとは具体的に何を意味するのでしょうか?

(左)散乱距離がピクセルより小さい場合 (右)散乱距離がピクセルより大きい場合

(Naty Hoffman 氏による SIGGRAPH 2013 の講義ノート で、シェーディングの計算に関する詳細をお読みいただけます)

この問いに対する答えは単純ではありません。上の 2 つの図にある緑色の円はひとつのピクセルを表しており、その中央に入射光が入って来ています。青い矢印は拡散光、オレンジ色の矢印は反射光を表します。左の図では、全ての拡散光がマテリアルの中で散乱した後、同ピクセルの境界内で再出現しています。通常レンダリングを行うマテリアルのほとんどは、この状態になります。この場合は、拡散光はその入って来た地点から出ていくという想定が確実に成立します。

問題は、右の図のように、拡散光が非常に大きく散乱するためにピクセルの範囲外から再出現してしまうようなマテリアルをレンダーする場合です。このような場合には上述の想定が成り立たず、これを解決するためには高度なテクニックが必要になります。

(左)拡散光、(右)反射光

最新鋭のリアルタイムサブサーフェス・スキャタリング技術によって、ライティングに特殊なスクリーンスペースブラーが施されています。ただし、これを行うには、拡散ライティング(拡散光)とスペキュラーライティング(反射光)をそれぞれのバッファ内に確実に分別する必要があります。これは何故でしょうか?上の図をもう一度ご覧ください。反射光は、表面下の現象に全く関係なく、表面でそのまま反射しているのが分かります。このことから、少なくとも表面下の近似値の計算が完了するまでは、スペキュラーライティング(反射光)は拡散ライティング(拡散光)と分けておく必要があるのです。

下のクローズアップしたキャプチャ画像で、分別されたライティングを分かりやすく確認できます。全ての反射光は完全に拡散光とは分けられているため、左側の放射照度/散乱バッファに必要な処理を、反射光の高周波ディテールを損なうリスクが全くない状態で行うことができます。

(左)拡散バッファ、(右) スペキュラーバッファ

ユーザーが作成したサブサーフェス・プロファイルで作成・送信された拡散カーネルを使用して、スクリーンスペースブラーが、このサブサーフェス・スキャタリング現象を近似します。

マテリアル別に異なるサブサーフェス・プロファイルを使用可能

計算の最後に反射光と組み合わせることで、当初の問題は無事解決できました。このようなテクニックは、ピクセル外で起こる散乱を近似する上で極めて効果的です。これで、シーン内のすべての不透明なオブジェクトにシェーディングが施されました。

すべての不透明なオブジェクトにシェーディングが施された状態

次のステップは、スクリーンスペースリフレクション(SSR)、スカイボックス、スクリーンスペースアンビエントオクルージョン(SSAO)、そして透明オブジェクトのレンダリングです。以下でこれらのパスを順番に確認することができます。

SSR、スカイボックス、SSAO、透明オブジェクトのレンダリング

モーションブラーの重要性

モーションブラーは ADAM 制作で非常に重要な役割を果たしました。繊細でシネマティックな表現のもうひとつの軸として、いくつものショットの鍵となった要素です。

(左)モーションベクターテクスチャー、 (右)モーションベクターを用いて計算されたモーションブラー

当然の事ながら、モーションブラーのレンダリングを行うためには、レンダラーがモーション自体の情報を把握している必要があります。この情報は、先に仮のモーションベクターテクスチャー(左)をレンダーすることで取得されます。このバッファは、スクリーン空間内の現在の頂点位置と以前の頂点位置の差分を計算することによって構築され、バッファからモーションブラーの計算で使用する速度を取り出すことができます。

モーションベクターを適切に Alembic ストリームから取得するために、一部特別な作業も行われました。私の同僚である Sean の最近の投稿 で、この作業に関して、およびその他 Alembic に関連事項の詳細をお読みいただけます。

Post-FX

最終的なポストプロセスの適用前と適用後

ついに、たっぷりシェーディングの施されたポストプロセッシングパスの段階です。ここでは、最終的なカラーグレーディング、 ACES トーンマッピング、ビネット、ブルーム、被写界深度が合成され、完成版に近い画が出来上がっています。ただ、ひとつ欠けているものがあります。マリアンのヘルメットのシールドはどこへ行ったのでしょう?

マリアンのヘルメットのシールド

リアルタイムグラフィックスで透過性を扱うことのある業界なら、その難しさは周知の事実でしょう。被写界深度、スクリーンスペースリフレクション、モーションブラー、アンビエントオクルージョンなど、エフェクトのバックボーンすべてについて、シーン深度からの空間認識および空間の再構成が必要となります。しかし、透明なオブジェクトの乗ったピクセルで、一体どうすればそんな事が可能なのでしょうか?深度の値が 2 つ以上必要になってしまうはずです。

これに対応する方法として採用されたのは、最初にすべての不透明なオブジェクトをシーンにレンダーし、次に透明なオブジェクトのリストの、空間の逆順フォワードパスをレンダーし、深度を書かずに各パスをフレームにブレンドするというものです。この方法なら、当の問題を効率的に無視して、キャラクターの眉や香煙など、ほとんどの要素を問題なく作り出すことが可能なのです。

マリアンのヘルメットのシールド。反射が非現実的、被写界深度が不正確、アンビエントオクルージョンもモーションブラーもなし

しかし、上の画像からお分かりの通り、問題を無視することではマリアンの透明シールドの問題は解決しません。このままでは、シネマティックな短編映像たるべき作品にも関わらず、作品の尺の半分近くがこのような状態を呈してしまいます。この例外的で厄介なケースに対応し得る何らかの代替策が緊急に求められる状況です。

ここで採られた方法は、完全にシェーディングされた 2 つのフレーム間のコンポジットパスへ透明度の計算を遅延させることでした。上記で確認した通り、1 番目のフレームにはマリアンのシールド以外の全てが含まれています。 1 番目のフレームのレンダリングの後に、2 番目のフレーム用の 2 回目のレンダーパスに G バッファと深度が配置され、この 2 番目のフレーム内でシールドが不透明なオブジェクトとしてレンダーされます。

シールドの透明度を、完全にシェーディングされた 2 つのフレーム間のコンポジットパスへと遅延

2 回目のポストプロセスパスを 2 番目のフレームで実行し、元々のフレームの G バッファと深度の内容が加わることで、シールドの適切な SSR、 SSAO、モーションブラー、被写界深度を求めることができます。元のフレームにマスクを戻すために必要なのは、シールドのアルファによって合成を行うことだけです。これはモーションブラーまたは被写界深度によってぼかされます。

このテクニックを適用した場合としていない場合の比較。右側にはオクルージョンが確認できます。

上記の手順により、上の比較画像で分かる通り、マリアンのシールドを綺麗に統合し戻すことができました。右側には適切な SSR と アンビエントオクルージョンの効果が確認できます。これで透過性の問題の全てが解決された訳では決してありませんが、上記のテクニックにより当初の厄介な問題は解決され、該当の透明オブジェクトにうまく適合したポストプロセッシングが実現されました。

最後の仕上げ ― フレア

Oats Studios は、自社のフレア・システムを効果的に使用して作品に素晴らしい仕上げを施し、映像のシネマティック品質を飛躍的に向上させました。このレンズフレアはタイムラインでアニメーション化およびシーケンス化され、これがフレームに付加的にブレンドされることで、映像の最終形が完成しました。

シェーディングが完了したフレームと、そこに追加されるレンズフレア

完成形

ここでは、本記事内で上述したすべての要素がランタイムでレンダーされています。

人質に取られた兄弟に石を持って近付くマリアン

以上、本記事ではフレーム分析についてお話させていただきました。フレーム分析を行うことで、特定のグラフィックチームが制作のニーズに応じてどんな方法を採用しているか把握でき、自分のプロジェクトに活かせる有益な情報を豊富に得ることができます。このような分析についてもっと知りたい方は、Adrian Courreges 氏による Graphics Study シリーズをご覧になることをお勧めします。色々な AAA 作品のフレーム分析が行われています。

今後の予定

Unity は 2018 年に(ADAM で使用されたサブサーフェス・スキャタリングなどの)より強化されたグラフィック機能を全てのユーザーの皆様にお届けする計画です。スクリプタブルレンダリングパイプライン(SRP)と呼ばれる新しい API のセットが 2018 年ベータ版で入手可能となっており、ユーザーの皆様ご自身でレンダラーの定義を行うことができます。また、高画質レンダリングパイプライン(HDRP)と呼ばれる SRP のテンプレート実装もリリース予定です。これはサブサーフェス・スキャタリングなどの素晴らしい新機能を含む最新のレンダラーです。ADAM で使用されたサブサーフェス・スキャタリングは、バージョン 2017.1 の従来のレンダラーに HDRP から直接ポーティングされたものです。

SRP や、グラフィック関連で今年 Unity が提供するラインナップについての詳細は、 Tim Cooper による記事『Unity 2018 とグラフィックス 』をご覧ください。

Unity について

Oats Studios をはじめ、映像制作の未来を変える様々なチームの開発を支えるUnity 2017 とその機能(タイムライン、Cinemachine、ポストプロセッシングスタック、 30 FPS のリアルタイムレンダリングなど)の詳細は、こちら をご覧ください。

返信する

これらの HTML タグや属性を使用できます: <a href=""> <b> <code> <pre>

  1. Thanks a lot for the post. It has helped me get some nice ideas. I hope I will see some really good result soon. geometry dash

  2. Good thing the Visor was concave, otherwise you might have needed even more GBuffers ;)

  3. That’s all fine and good. But how does your average user do any of that?

    1. The goal of having Scriptable Render Pipeline is for regular users can use basic package and creating some beautiful results.
      It’s coming!

    2. Dude this is actually an incredibly detailed and in-depth article about the current and, quite likely future graphics pipeline in Unity. To “do any of that” you do the same things as with everything else: you learn and this here is a very good read just for that.

  4. Hey guys i’ve been doing some work taking just the gbuffer and doing alot of custom shading using the data. I too wan’t to implement a translucency effect in a deferred manner. Did you just hijack one of the empty gbuffer channels? If not, how did you get deferred rendering to write to an additional buffer without re-rendering? If the answer is just to use Scriptable Render Pipeline – any idea how you’d do that? The docs just obsessively described culling.

    1. Hey buddy, Scriptable Render Pipeline is still under development, you can give you a try and download from here: https://github.com/Unity-Technologies/ScriptableRenderPipeline
      but, I think it’s better to wait.

    2. Well, I did not try this, but I am almost sure that if you set up two cameras and two layers (one for each camera), set the cameras to only render the objects in their respective layer (first camera renders layer Alpha, second camera renders layer Omega) and then clone the Visor to be two objects: one transparent for layer Alpha and one opaque for layer Omega you get what they got. Then I guess you forego the lighting pass in the “opaque” case and only use the “opaque” GBuffer during a custom transparent shader (i assume?) pass. Keep in mind, in the case of a Visor this was only possible because the geometry of a visor is concave, that is, no matter how the camera is positioned, the Visor only renders one layer per pixel. If the Visor was a complex object with cavities (say like a semi-transparent ghost figure of a person), they would have needed way way more GBuffers than just 2 :)

  5. nice … really needs to an SSS shader in unreal :]
    what about releasing of an good eye shader too??