イベント
[CEDEC 2016]UE4を扱うアーティストがつまづき易いポイントはここだ。Epic Gamesが解説する注意点と回避法
講演を担当したのは,Epic Games JapanのSenior Support Engineerの篠山範明氏。非常にボリュームがあり,密度も濃い内容だったので,概要のレポートだけでもかなりの長編になってしまったが,興味のある人はお付き合いいただきたい。本稿でも講演の半分程度しか触れていないので,詳しく知りたい人は,インターネット上で公開されたスライドの全文を参照することをお勧めする。
SlideShareで公開中の「Unreal Engine 4のレンダリングフロー総おさらい」
G-Buffer書き出しフェーズの「Base Pass」における注意点
篠山氏によるセッションは,冒頭でUE4のレンダリングパイプライン全体のフローチャートを示したうえで,フローチャート上の各機能ブロックに関係したテクニックを紹介するという流れで行われた。
篠山氏がまず説明したのは,フローチャートの先頭……ではなく2番めにある「Base Pass」からだ。Base Passとは,「G-Buffer」(中間バッファ)の生成に相当する。
いわば,素のポリゴンを先行して描画するようなイメージだが,実際にはポリゴンだけではなく,ライティングやシェーディングに必要な中間パラメータも合わせて出力している。この出力先は,複数のバッファになるのが一般的だが,これらのバッファをG-Bufferと呼ぶ。
なぜそんな面倒なことをするかといえば,Deferred Renderingには2つの利点があるからだ。1つめは,とても複雑なライティングやシェーディングを1ピクセルにつき1回行うだけですむこと。2つめは,ライティングに用いる動的な光源数に明確な制限がないため,リッチなライティングが行えるということである。
複雑なシーンを描画するときにたくさんの光源を使って複雑なシェーダを動かす現代的なゲームグラフィックスの場合,最終的には画面に出力することなく,上書きされたり破棄されたりするピクセルをピクセルシェーダで処理することは大きな無駄で,性能を落とす原因になる。これを極力避けようとして開発されたのが,Deferred Renderingという方式なのである。
ここまでを踏まえたうえで篠山氏は,このG-Bufferを出力するBase Passで注意すべき点を2つ挙げた。
1つは,「視界に入ってこないオブジェクトは,描画対象にしない」というシンプルなものだ。
この「視界外カリング」(Frustum Culling)という処理は,一般的にはCPU側で行うもので,オブジェクトが視界内にあるのか外にあるのかの判定は,「バウンス」(Bounding sphere)と呼ばれる各3Dオブジェクト全体を覆うようにあらかじめ設定した球状境界に対して行われる。
ところが篠山氏によれば,このバウンスが誤って(あるいは適当に)設定されているケースがあるという。その場合は正しいカリングが行われないので,最終的には描画されないのに,GPUの描画対象としてレンダリングパイプラインに投入されてしまい,無駄なGPU負荷となってしまう。
どのくらいカリングに成功したかは,Stat InitViewsコマンドの「Frustum Culled Primitives」欄で確認可能だ。
また,FreezeRenderingコマンドを実行してからカメラを動かすと,視界外に何が描画されているかを確認できる。もし,その時点で視界外となる部分にカリングすべき3Dオブジェクトが描画されていたら,それは何らかの理由でカリングできていないことを示しているわけだ。その場合,注意深くバウンス設定などを確認する必要があると,篠山氏は説明していた。
Base Passにおける2つめの注意点は,「無駄なピクセルを計算させない」だ。
グラフィックス処理の流れでは,ピクセルシェーダで実際に処理を行う前の段階で,深度テスト(Zテスト)を行う「Pre Zテスト」というものがある。これから処理に取りかかろうとしているピクセルが,何かに遮蔽されるといった理由で描画されないことが明らかな場合,これを破棄するという処理だ。
しかし,半透明オブジェクトのようにαテストをともなう描画だったり,視差遮蔽マッピングのようなピクセルシェーダ処理後に深度値が書き換わったりするような場合,そのオブジェクトはPre Zテストが行われず,処理からバイパスされてしまう。
しかし,不透明オブジェクトでも,Pre Zテストを回避してしまうよう設定してしまうと,Pre Zテストで破棄できるはずなのに破棄されなくなってしまう。この場合,「ピクセルシェーダで処理されるが,(そのあとの)Zテストに失敗して破棄される」ので,描画結果はおかしくならないものの,性能面ではワーストケースに落ち込むと篠山氏は指摘する。
Z値の先だしフェーズ「Z PrePass」での注意点
次のテーマは,「Z PrePass」である。UE4のレンダリングパイプラインでは,
事前に深度値を先出しする目的は,最終的な映像と同じ深度バッファの内容結果を,レンダリング前に得ることにある。Z PrePassに続くBase Passでは,この先出しした深度値バッファを参照してPre Zテストを行えるので,最終的に画面には残らないピクセル(≒上書きされて消えてしまうピクセル)に対するピクセルシェーダの実行を避けられるという理屈だ。
UE4では,背景オブジェクトのように静的なオブジェクトだけをZ PrePassに含めて,動的なキャラクターなどはZ PrePassに含めないのが,デフォルト設定となっている。しかしUE4の場合,プロジェクトファイルの「Early Z-pass」および「Movables in early Z-pass」という設定で,これをカスタマイズできるのだ。
あるいは,「Use as Occluder」設定にチェックを入れることで,各オブジェクト単位でZ PrePassに含めるか含めないかを設定することも可能である。
篠山氏は,「通常はデフォルト設定のままでいいはずだが,ポリゴン数の多いシーンでは,Z PrePassの実行自体がGPUに対して高負荷になってくる場合もあるため,そうしたシーンでは,前出の2つの設定をオン/オフしたうえで性能比較をするとよいかもしれない」と述べていた。
ライティングの前段処理系「Pre-Lighting」での注意点
UE4では,血しぶきや弾痕といった投射テクスチャマッピングで行うデカール描画は,ライティング(Lighting)の前,すなわち「Pre-Lighting」の部分で処理する設計になっている。これは,後段のライティング処理によって,デカールがライティング対象に含まれる可能性があるためだ。
デカールと同様に,「全方位から差し込む仮想的な環境光に対する遮蔽率」(Ambient Occlusion,アンビエントオクルージョン)の処理も,Pre-Lightingで行う。
篠山氏によれば,このPre-Lighting描画に関連するトラブルでは,「デカールが焼き込みのライトマップで上書きされてしまう」とか,「スカイライトを配置するとデカールの下が透けて見える」というのが定番となっているようだ。
結論から言えば,これは「UE4の仕様」ということなのだが,もちろん回避する策も提供されている。それが「DBuffer Decals」という機能だ。
DBuffer DecalsまたはDeferred Decalとは,デカールをG-Bufferとは別の,「D-Buffer」と呼ばれるデカール専用特殊バッファに描画しておき,G-Bufferを用いてのライティングやシェーディングは,D-Bufferの内容も反映しながら行うという流れになる。デカールの描画結果がG-Bufferに統合されてしまうと,これが原因で前出のような問題が出てしまう。これを回避するために専用バッファを用意したというわけだ。
ただ,D-Bufferの利用に加えて,描画系も今までより処理のパスが増えるため,描画負荷が高くなるのがネックといったところ。
負荷は高くなるが正確なデカール表現が行えるDBuffer Decalsを導入すべきなのか。あるいは別の表現を選んでDBuffer Decalを避けるかは,よく検討して決めたほうがいいようだ。
光源周りの扱いで性能が変わる「Lighting」フェーズ
次に篠山氏が説明したのは,ライティングの段階である。UE4のライティングでは,「Static」「Stationary」「Movable」という3種類のライティングカテゴリが用意されている。
最初のStaticは,すべて事前計算したライトマップ的な処理で行う静的なライティングだ。この事前計算には,間接光まで配慮したライティングを計算できる専用レイトレーサー「Lightmass」を使って制作できる。
2つめのStationaryは,光源からのライティング自体は実行時にリアルタイムで行うが,影の計算だけは事前に処理しておく方式だ。もう少し具体的にいうと,Stationary光源とは,UE4のグラフィックスエンジン内部では普通の動的光源と同等の扱いをされるが,シーンに配置されると基本的には動かない光源のこと。動かないので,影生成用のシャドウマップ生成も事前計算で扱える。
ただ,ちょっと複雑なのだが,この仕組みが性能面でメリットとなるのは,,配置したStationary光源が1グループあたり4つまでという,内部処理的な制限があることだ。
Stationaryでは,事前生成するシャドウマップ(テクスチャ)が,1光源あたり,αRGBの4チャネルピクセルフォーマットのそれぞれに割り当てられてられる。つまり,1つめの光源はシャドウマップのαチャネル,2つめの光源はRチャネルにといった具合だ。αRGBだと4チャネルまでしか対応できないため,1グループあたり4つまでに制限されると,篠山氏は説明していた。
では,5つめの動的光源を設定したStationaryは,どうなるかというと,完全な動的ライティングであるMovableとして扱われるため,Stationaryとして定義しても性能向上にはならないという。
そして,最後のMovableは,先述したように影生成も含めて,完全にすべて実行時に処理する動的光源である。
さて,Base Passの説明でDeferred Renderingは,動的な光源数に制限がないと述べたが,実際の性能面ではどうなのだろうか。
篠山氏は,「照射影響範囲が狭い動的光源を100個置いたシーンと,照射範囲の大きい動的光源を8個置いたシーンで,どちらが負荷が高いでしょうか?」というクイズを出題する。
答えは,後者(スライドの右)のほうが高負荷であるという。
ここで言う負荷が高いとは,計算量が多いともいえる。そして計算量の大小は,シーン内の各ピクセルが,何個の光源による照射に影響されるかで決まってくるものだ。そのため,1ピクセルが1光源から照射されるものが8つあるよりも,1ピクセルが8光源から照射されるほうが,処理負荷は高いのである。
映り込みや鏡面反射を司る「Reflection」に正解はあるのか?
UE4に限ったことではないが,リアルタイムのゲームグラフィックスで,何かと取り扱いにコツがいるのが「Reflection」だ。
金属のような鏡面反射の強い材質であれば,その周囲の情景が映り込む。こうした映り込みは,素材側から見れば,全方位から照射してくる全周光源のようなものだ。Reflectionは,いわばこの全周光源に相当するもので,素材の材質を表現するときの説得力に大きく関わる重要な要素といえよう。
さてUE4の場合,鏡像や映り込み情景のような素材,Reflectionの生成手法/取扱手法は,3種類が用意されている。
1つは,事前生成で作る完全に「静的なReflection」だ。
事前生成を行うポイントは,シーンにおける任意の座標位置に設定可能で,そこから全方位のレンダリングを行い,結果はキューブマップといったテクスチャとして生成される。ちなみに,全方位情景の事前生成ポイントは「Reflection Probe」と呼ばれる。
篠山氏によれば,問題となるのは,「Reflection Probeで取得したこの全方位情景を,シーン内のどの範囲に影響させるかという部分」であるという。
氏は,先述した動的光源の負荷と同じような例を挙げて説明する。1シーンに1個の静的Reflectionを配置して,これがシーン内のすべてに影響するように設定した場合と,200個を配置して,それぞれがごく狭い範囲に影響するよう設定した場合とでは,描画負荷は大差がないというのだ。
しかし,200個設置のケースで,それぞれの影響範囲を大きく取った場合は,途端に負荷が増大する。
理由は動的光源の負荷と同じだ。数の多い静的Reflectionの影響範囲を広くしてしまうと,1ピクセルを描画するのに多くの静的Reflectionを参照して計算しなくてならず,それによって負荷が増大してしまう。
そもそも,Reflection Probeを隣接して設定しているのに,その影響範囲を互いにオーバーラップすることにそれほど深い意味はないと,篠山氏は説明する。Reflection Probeの配置と影響範囲の設定は,理に叶ったものにしなければ,無駄に負荷を増やすだけというわけだ。
そこで篠山氏が紹介した設置の具体例は,いうなれば「Level of Detail」(LoD)的な意味合いを持たせるというものだ。
3Dシーン全体を覆う大まかな静的Reflectionを1つ設定したうえで,シーンの大きなエリアごとに,影響範囲の広い静的Reflectionを設定する。それに加えて,ライティングやシェーディングに大きな影響を及ぼしそうなものがあれば,そこに局所的な静的Reflectionを設定するといった具合だ。
2つめのReflectionは「Screen Space Reflection」,通称SSRである。SSRは,レンダリング結果に対して,画面座標系での局所的なレイトレーシングを行い,Reflectionをランタイム上で,なおかつリアルタイムに生成するという事前計算なしの手法だ。
シーンの一瞬を切り取っても,動的キャラクターや動的光源の影響などを正確に反映したReflectionが得られるのは利点であるが,画面外に存在する情景の影響は,一切,無視されてしまう欠点もある。
SSRによる典型的なエラーは,この弱点が露呈したときに起こりがちだ。これを補うのに有効なのは,静的Reflectionを組み合わせることであるという。
3つめのReflectionは,「Planar Reflection」だ。
これは,平面に映り込む情景を正確に描画できるReflectionの手法である。下の図版を例に説明すると,映り込み対象の面(たとえば水面)に対して,水面に潜った位置に視点(E’)を置き,上下を逆転してシーンをレンダリングするようなものだ。そのシーンを2回描画しているような処理を行うため,必然的にかなり高負荷なReflection処理となる。
篠山氏は,このPlanar Reflectionについて,「カットシーンなど,クオリティが必要な場面での活用を推奨する」と述べていた。実際,GDC 2016で公開された「Hellblade: Senua's Sacrifice」(PC / PS4)のカットシーンにおける水面への映り込みは,Planar Reflectionを使って生成されているそうだ。
半透明にまつわるエトセトラ
UE4に限った話ではないが,半透明オブジェクトの描画は,リアルタイムグラフィックスにおいては厄介なテーマである。「UE4でも,半透明オブジェクト描画に関連した厄介ごとは少なくない」と,篠山氏は正直に述べていた。
とくに身近で厄介な半透明オブジェクトは,パーティクルエフェクトである。ゲームグラフィックスに携わる日本のアーティストは,パーティクルエフェクトが大好物なので,この問題への関心は高いのではないだろうか。
UE4の場合,半透明パーティクルの描画時には,深度値(Depth)の更新を行わないという。
この場合に起こりうる問題といえば,深度値をキーにして行うポストプロセスがおかしくなるというものだ。
下に掲載したスライドは,数m先の炎にフォーカスが合っていて,それより奥は被写界深度表現でボケ味を与えたという画像だが,描かれた炎のパーティクルが,地平線を境界線にして上側だけがボケて,地平線の下側はボケていないという変な描画になってしまっている。
これは,炎パーティクルに対応する深度値がなく,地面の描画に対応する深度値しか深度バッファ(Zバッファ)に存在しないことが原因だ。地平線から上の背景は非常に遠いので,背景も炎もまとめて焦点から大きくはずれていると判定されて,同じ被写界深度でボカされるという理屈である。ボケフィルター側からすれば,炎パーティクルが描かれているかどうか知りようがないので,結果的にこんな有様になっているのだ。
篠山氏は,こうした描画不具合に対する解決策として「Separate
Separate Translucencyでは,半透明パーティクル描画時に深度値の更新を行わないことは変わらないものの,被写界深度表現のようなポストプロセス処理から切り離された別のバッファに,半透明オブジェクトを描画できるという。
いわば,半透明オブジェクトと不透明オブジェクトとの描画を分離(Separate)させて,ポストプロセスは不透明オブジェクトの描画先だけに適用するような処理系とするわけだ。
このテクニックを使うと,半透明パーティクルに対する変なボケの適用は回避できるのだが,半透明パーティクルに対して適切なボケを与えられない。問題の回避はできても,適切な処理は行えていないのである。
この点について篠山氏は,現在のUE4における仕様制限のひとつであると述べていた。現在,UE4のエンジニアやベテランユーザーの間で,うまい解決策がないか議論中だとのことなので,それが見出されることに期待しよう。
さて,半透明描画にまつわる厄介ごとには,もうひとつ定番の問題がある。それは描画負荷が高くなりがちという問題だ。
ここでも篠山氏はクイズを出題した。「画面全体の色を変更するポストエフェクトと,画面の一部に立ち上る煙のパーティクルエフェクトのどちらが高負荷でしょう」というものだ。
単純に考えれば,画面全体のほうが処理するピクセル数が多そうに思えるが,答えは右だ。
一見すると,半透明パーティクルの煙は画面の一部にしか描かれていないように見えるが,実は何度も重ね描きされている。それは,パーティクルをワイヤーフレーム表示した画面を見ると一目瞭然だ。画面内の同一領域に,煙パーティクルが大きさを変えて,何度も重ね描きされている。
篠山氏は,この半透明パーティクルの重なり具合を視覚化するテクニックとして,「Shader Complexity」のデバッグビューを紹介した。
このデバッグビューは,ヘビーに反復描画されている領域を赤く表示して可視化することで,問題が生じている部分を判断できるというもの。赤い部分があれば,パーティクル描画の負荷を低減すべきであるというわけだ。
パーティクル描画の負荷低減策として最も効果的でシンプルなテクニックとして,篠山氏は,別バッファに分離してパーティクル描画を行わせるSeparate Translucencyを,低解像度で描画するというやり方を説明した。
反復描画によって,重ね描きする半透明パーティクルを低解像度で描画したうえで,メインのレンダリング結果と合成するときに,適当なサイズに拡大して合成するわけだ。半透明パーティクルの描画先バッファを縦横半分の解像度にすれば,単純計算で描画負荷は4分の1にできる。その半分ならば16分の1で済む。
低解像度バッファに描画し,それを拡大してから合成するということは,パーティクルの輪郭が強くボケるわけだが,もともと半透明パーティクルはもやっとしたものなので,影響は少ないだろうというのが篠山氏の考えである。あまり解像度を下げすぎると,解像度の不一致が露呈するので,バッファの描画解像度は慎重に選ぶ必要があるものの,処理負荷軽減効果は大きいとのことだ。
篠山氏は,パーティクル描画の負荷低減策として,もう1つのテクニックを提示した。それは,パーティクルアニメーションなどで,半透明領域ではなく完全透明領域が多い場合に利用できる「Particle CutOut」だ。
通常のパーティクルは,2枚のポリゴン(三角形,または四辺形)で構成されるわけだが,完全に透明な領域を避けるように,複数のポリゴンに自動分割して描画するというのが,Particle CutOutの考え方となる。
ポリゴン数は増えることになるが,無意味なピクセルシェーダに実行を避ける効果が期待できるため,大量のパーティクル描画を行う状況では,負荷低減に結びつけられるかもしれないという理屈だ。
処理順序が固定されているUE4のポストプロセス
描画結果に対して,最後にフォトレタッチをするような感覚で加工を行うのがポストプロセスだ。
フォトレタッチは通常,2Dの写真に対して行うものだが,3Dのゲームグラフィックスにおいては,レンダリング結果に付随するさまざまな情報を用いて,3次元の加工を施すことができることが大きな違いである。
UE4は,色変調や被写界深度表現,ブルームなど,画像をフォトリアルな見た目に加工するための,さまざまなポストプロセスエフェクトを備えている。
UE4で特徴的なのは,ポストプロセスの効果を3Dシーン内の特定位置,またはカメラに設定できるところだ。たとえば,「3Dシーンのとある場所に入ってきたら被写界深度表現がかかる」とか,「このカメラで描画したときにはセピア調になる」といった処理が実現できるのである。
篠山氏は,UE4特有のポストプロセスの処理順序については,事前に覚えておくといいとアドバイスしていた。この処理順序は変更できず,必ずこの流れで実行されるためだ。
たとえば,「モーションブラーを先に行ってから被写界深度表現を行う」ことはできない。もし,設定したパラメータに対して,想像と違う結果になった場合は,この処理順序に依存した問題である可能性を疑ったほうがいいかもしれない。
余談気味だが,篠山氏は,UE4のツール上では公開されていない,コマンドラインによるポストプロセスのパラメータ設定手法があることにも言及している。
セッションではその一例として「r.BloomQuality」という,ブルーム効果の広がりを設定できるパラメータを紹介した。
こうした隠しコマンド的なコマンドパラメータは,どのように見つければいいのか。以前であれば,UE4のソースコードを開いて,そのコメント欄を探すしかないという,正真正銘の隠しコマンドだった。しかしCEDEC 2016時点で最新版となるUE 4.13には,コマンドラインによるパラメータのヘルプをHTMLファイルに出力する「Console Variables」という仕組みが実装されたという。
すべて英語表記ではあるが,これらの隠しコマンドによるパラメータを駆使することで,より深いポストプロセスのコントロールを行ったり,処理の順序依存を克服するような設定を行えるかもしれないと,篠山氏は述べていた。
UE4を使うアーティストにとって実用的なセッション
UE4は,プログラミングにそれほど明るくないアーティストであっても,容易にゲーム開発ができるゲームエンジンといわれる。ただ,リッチなグラフィックス表現を追い求めれば,いくらでも凝ることができるため,「せっかく作り込んだはいいが,いつのまにか性能が出ない……」という状況になってしまうことも少なくないようだ。
そんなときに役立つのが,グラフィックスエンジンの処理系に対する理解である。処理系に対する知識が多少なりともあれば,重くなりにくい表現の制作も可能になるだろうし,性能を犠牲にしなくても高いレベルの表現にも挑戦できるようになるかもしれない。その意味では,篠山氏による今回のセッションは,かなり有用だったのではないだろうか。
実際,セッション後に筆者が聴講者に軽くヒアリングしてみたところ,UE4を使ってゲーム開発に携わる人がつまずく代表的な課題が一通り網羅されていて,とても有用なセッションだったという声が聞かれた。ぜひセッションの内容を生かして,よりよいゲーム作りに取り組んでいただきたい。
SlideShareで公開中の「Unreal Engine 4のレンダリングフロー総おさらい」
CEDEC 2016 公式Webサイト
4GamerのCEDEC 2016関連記事一覧
- 関連タイトル:
Unreal Engine
- この記事のURL: