Game Developers Conference 2012レポート

【GDC 2012】西川善司の3Dゲームファンのための「inFamous 2」講座
素晴らしくも不完全なSSAO。その弱点を補足する技術に注目!


3月5日~9日開催(現地時間)

会場:San Francisco Moscone Center


 


 3Dゲームグラフィックスの大きなテーマに「影」と「陰」がある。最近では「影」生成はデプスシャドウマップ技法が主流で、影生成のためのシャドウマップを複数用いて、それらを視点からの距離の遠近に応じて割り当てるカスケード拡張技法が主流になりつつある。こうしたアプローチの影生成は、生成するシャドウマップの解像度に依存した精度になり、微細な影を作り出すことができない。

 また、間接光などの二次光源からの影生成は、現行の手法では絶望的だ。そこで、最近脚光を浴びてきたのがレンダリング結果と伴って出力されるデプス(深度)バッファの内容を元にして、ポストプロセス的に仮想的な環境光に対する「陰」を生成するSSAO(Screen Space Ambient Occlusion)だ。

 このSSAOは、Crytekによって考案され、瞬く間に様々なタイトルで採用されたが、同時に細かい問題が浮き彫りにされてきた。ここ最近のGDCでは、このSSAOの改良にまつわるセッションが多くなってきており、今年も例外ではなかった。「Ambient Occlusion Fields and Decals in Infamous 2」もそんなセッションの1つだ。非常に理解しやすく、しかも実装が簡単そうなので紹介しておきたい。


【inFamous 2】



■ Screen Space Ambient Occlusion(SSAO)は完璧ではない!

Nathan Reed氏(Rendering Programmer、Sucker Punch Productions)

 PS3専用タイトルの「inFAMOUS 2(インファマス2)」では、焼き込みの「陰」として、頂点単位に自己遮蔽具合を焼き込んだ古典的なAO(Ambient Occlusion)とSSAOの両方を使用してリッチな“陰”表現を行なった。

 頂点単位のAOとSSAOは、それぞれの短所と長所をかばい合う、よい補完関係にある。頂点単位のAOは、動かない静的なオブジェクト、それも巨大な建物が作り出すような大局的な陰(影)生成には有効だ。その陰(影)の投射先の頂点をあらかじめ細かくしておかなければならない面倒くささはあるが、SSAOでは作り出しにくい広範囲の陰(影)を作り出すには都合がいい。これを補間する存在がSSAOになる。

 SSAOは静的なオブジェクトはもちろん、動き回る動的なオブシェクトに対して局所的な陰(影)成が行なえる。具体的なアルゴリズムは以下のような感じだ。

 着目している画素がどのくらい遮蔽されている場所の画素なのかを、その画素に対応する深度値とその画素周辺に対応する深度値を読み出して、その値同士を比較して求める。その比較演算から、深度マップ上の奥まった閉ざされた場所にあればある画素ほど遮蔽度が高いとして“陰”色が付けられる。格好良く言えば、深度マップ上で超ローカルなレイキャスティングを行なって遮蔽具合を探査する手法だといえる。

 しかし、SSAOは、結局はレンダリング結果に対して行なう画面座標系ポストプロセスであるため幾つかの弱点がある。まず、レンダリング結果の外周付近は(深度バッファの)情報が足りないため遮蔽度がちゃんと計算できない。陰色が薄くなるだけなので無視されているケースがほとんどとなる。また、深度マップは視点から見た奥行き情報しか格納していないので、何かに遮蔽されている背後の遮蔽度は計算できない。

 それと、シーンやオブジェクトに対して視点までの距離がほぼ一定のゲームであればバレにくいが、シーンやオブジェクトに対して視点を近づけたり遠ざけたりすると不自然さに気がつかれる。そう、SSAOで付けられる陰色の範囲(大きさ)は、画面座標系での遮蔽度の探査が行なわれるだけなので、描かれる陰色の領域は画面上の面積に対してほぼ一定になってしまうのだ。

 例えば、ある棒状のオブジェクトの根本に付いている“陰”色の太さに着目したとすると、そのオブシェクトにひっ付きそうなくらい近づいて見ても、遠く離れて見ても、画面上に描かれる根本の陰の太さが変わらないのだ。正しくは、近づいたときはその棒の根本が拡大して見られるのだから、その“陰”色は太く見えなければならないし、遠く離れたときには棒が小さく見えるのだからそれに合わせて細く見えなければならない。




■ AO Fieldsという考え方

「inFAMOUS 2」におけるAO Fieldsの仕様

 「inFAMOUS 2」は三人称視点のオープンワールド型のアクションゲームで、比較的、カメラがシーンやオブジェクトに対して近づいたり遠ざかったりできるシステムになっている。しかも、超能力を使って、車のような大きなオブジェクトを大胆に投げたり移動できたりできるため、前述したSSAOの弱点はクリティカルに見える状況がある。

 そこで「inFAMOUS 2」開発チームは、SSAOを独自のアプローチで改良することにした。その1つが「Ambient Occlusion Fields」(AO Fields)だ。この手法では、各オブジェクトモデルに対になるAO Fieldsを事前計算してやる必要がある。AO Fieldsとは感覚的には、「そのオブジェクトが全方位にどう他者を遮蔽するか」という情報になる。

 このAO Fieldsの大きさは、各オブジェクトモデルをすっぽりと覆える直方体よりも、さらに一回り大きい直方体とする。この大きい直方体を、xyz各軸8~16分割したボクセルで分割して、その各ボクセルの中心から全方位(6方向)に対して32×32テクセルのキューブマップをレンダリングしてやる。


【AO Fields】
AO Fieldsのイメージ(左)、各ボクセルでのAO Fieldsは、各ボクセルの中心からみた全方位の情景をキューブマップにレンダリングした結果から求める(右)

 ここからはかなり大胆だ。レンダリングされたキューブマップを探査して、その重心を求め、ボクセルの中心からこの重心への方向を算出。これを遮蔽ベクトルとする。ちなみにここでいう「重心」というのは、キューブマップに描かれ塗りつぶされたテクセルの面積からその中心を求めるだけだ。これで、このボクセルの中心から見た「遮蔽方向≒1番この地点が遮蔽されている方向」が求まる。

 ところで、このキューブマップにレンダリングされたテクセルの面積(塗りつぶされた面積)は、このボクセルの遮蔽率を表している。この面積を元に「この地点の遮蔽率」を求める。こうして求めた遮蔽方向(x、y、z)と遮蔽率をテクスチャに格納する。遮蔽方向はベクトルなのでRGBに格納し、遮蔽率はスカラなのでαに格納する。こうして計算されてできるのが、3Dテクスチャ(3次元の数値テーブル)で、これがそのオブジェクトに対になるAO Fieldsということになる。

【AO Fieldsの事前計算】

 この「inFAMOUS 2」のAO Fieldsの考え方はMattias Malmer氏らの論文「Fast Precomputed Ambient Occlusion for Proximity Shadows」を参考にしており、この論文を参考にAO Fieldsの直方体サイズは決定したとのことだ。

 セッションでは、実際の「inFAMOUS 2」上のオブジェクトで、AO Fieldsをどのくらいの大きさで切ったのかも具体値が示されている。例えば車では、32×16×8のボクセルで区切り、各データが32ビット整数だったので3Dテクスチャとしての容量は16,384バイト(16KB)となっている。同様に公園の椅子は16×8×8ボクセルで4KB、ゴミ箱は8×8×8ボクセルで2KB。3Dテクスチャといってもだいぶ粗いため、意外にサイズは小さい。ただ、サイズは小さいが、データ自体はベクトルデータであり精度が要求されるためテクスチャ圧縮(DXT)は掛けていないとのこと。

【AO Fieldsの大きさ】
「他者に及ぼす遮蔽具合」を求めるのでオブジェクトサイズよりも必然的に大きい領域で考える必要がある。図のグレーの箱がオブジェクトを覆うジャストサイズの直方体だとすると、青い箱がAO Fields。「inFAMOUS 2」では参考論文に倣ってε=0.25を採択



■ AO Fieldsを用いてのレンダリング

ここまでをそのまま実装した結果。陰色が粗い四角状になって表れた。このままでは不自然過ぎる

 実際に、このAO Fieldsをランタイムでどう利用するかだが、全てのオブジェクト群をレンダリングし終わったあとに、各オブジェクトのAO Fieldsサイズの直方体をレンダリングしていく。

 といっても実際に直方体の実体を描くのではなく、深度テストを行なって、AO Fieldsサイズの直方体と全オブジェクトレンダリング済みのシーンとの交叉箇所(AO Fieldsを適用できる箇所)を求めるのが主たる目的だ。

 これはDeferred系レンダリングのライティングフェーズとよく似た処理系だ。Deferred系レンダリングのライティングフェーズでは「光源の適用範囲」を求めるが、AO Fieldsでは「遮蔽の適用範囲」を求めているわけだ。

 実際に描画を行なうピクセルシェーダーでは、深度テストの結果で、その箇所がAO Fieldsの適用すべき場所であると判断できた場合は、その深度値を元にワールド座標系の位置情報を逆算し、この値からさらにAO Fieldsのどのボクセルの影響下にあるのか(オブジェクトのローカル座標)を求める。

 AO Fieldsのどのボクセルを読み出せばいいかが求まったら、そのボクセルに対応する3Dテクスチャから遮蔽ベクトルと遮蔽率を読み出す。あとは、その箇所の法線ベクトルを読みだし(Deferred系のレンダリングエンジンならば深度バッファと共に先にGバッファとしてレンダリング済み)、この遮蔽ベクトルとの角度関係を吟味しつつ、遮蔽率でバイアスして陰色を算出すればいい。

 ここまでの内容を実際に実行したのが右記のスクリーンショットで、案の定、粗いボクセル単位で管理されているAO Fieldsの内容が、四角っぽい描画結果に如実に表れてしまっている。


【遮蔽度の算出式】
その箇所の遮蔽度の算出式。Strengthは遮蔽強度を表し、これはアーティストがオブジェクトごとに決定できる。つまりAO Fieldsによって付く陰色の濃淡をStrengthでコントロールできる(左)、その箇所の法線ベクトルが、遮蔽ベクトルの方向に近ければ近いほど陰色は濃くなるという調整項(右)



■ AO Fieldsの改良

最外殻境界ボクセルに向けて遮蔽率がゼロになるようにAO Fieldsを調整。このスライドでalphaとなっているのはαに格納されている遮蔽率のこと

 そこで、各オブジェクトのAO Fieldsに対し、最外殻境界ボクセルの遮蔽率を強制的にゼロとして、AO Fieldsの遮蔽率の1番大きい値から外郭ボクセルの遮蔽率ゼロに向けて線形的に減衰させる調整を施すことにした。この調整を行なったのが、このスクリーンショットで、四角っぽいアーティファクトはほとんど分からなくなっている。


【AO Fieldsを調整】
左が調整前で、右が調整後。四角っぽいアーティファクトはほとんど減退できた。

 しかし、アーティファクトは、もうひとつ、あった。それは、誤った陰色が付くというアーティファクトだ。そのわかりやすい例として、車のボンネットや屋根、サイドステップ付近に不要な陰色が付いていることが示された。

 これは、AO Fieldsがかなり粗いボクセル単位で計算されているためで、オブジェクトの境界面付近では遮蔽率が激変するため、この付近での遮蔽率の計算に多くの誤差が出てしまうのだ。

 各ボクセルで遮蔽項を計算する際に、そのボクセルが境界面にあるときは自動で遮蔽率を調整するようなAO Fields計算法を盛り込むことも考えたが、複雑な形状箇所ではそれも判断が難しい。そこで仕方なく、陰色を付けるピクセルの箇所の法線ベクトルを参照し、その方向に沿って、AO Fieldsの参照を半ボクセルずらす調整(オフセット)を加えるというハックを盛り込んだ。

 やや強引な対応だったが、効果は上々。境界面付近での意味不明な陰色は消し去ることに成功。ただし、その副作用として、遮蔽率の高い箇所での陰色がより濃く出るようになってしまった。ただ、まぁ、これは大きな問題ではないと判断。最終的にはこのハックを有効とすることに。ここまでの内容を映像にまとめられたものが、下になる。

【AO Fields映像】
オブジェクトの周囲に出ている陰色はSSAOによるものではなく、AO Fieldsによるものだ。SSAOは、この映像では意図的にキャンセルされているとのこと

【AO Fieldsを調整 その2】
境界面での不要な陰色。丸で囲んだところが該当箇所だ(左)。シェーディング対象箇所の法線ベクトルの向きに沿って半ボクセルずらしてAO Fieldsをサンプルするハックを盛り込むことでこのアーティファクトを除去(右)



■ AO Decalsという発想~厚みの薄いオブシェクトのためのAO Fields

 窓やドアなどの厚みの薄いオブシェクトに関しては、より簡略化したモデルのAO Fieldsを「inFAMOUS 2」では採用したという。基本的な考え方はAO Fieldsと同じで、対象オブジェクトをボクセル化して区切って、その各ボクセル単位で遮蔽項を求めていくのだが、薄いオブジェクトなので大胆な簡略を導入する。

 まず、3Dテクスチャは用いず、AO情報は2Dテクスチャで取り扱う。そして、ボクセルのレイヤーは4層に限定してしまい、1層目の情報をRテクセルに、2、3、4層目の情報をそれぞれG、B、αに格納する。

 各値がスカラになってしまうので、AO Fieldsで格納していた遮蔽方向のベクトルは切り捨て、遮蔽率だけを格納するようにするのだ。このオブジェクトの4層分の遮蔽率を格納したテクスチャをAO Decalsと呼ぶ。

 AO Decalsでは、遮蔽方向は各ボクセルで同一方向を向いていると仮定してしまう。例えば、例えば窓ならば、壁に取り付けられるので、遮蔽方向は壁面からみて屋外方向への半球方向だけで考えるのだ(反対側は常に壁で遮蔽されているとみなす)。

 AO Decalsでも事前計算として遮蔽率の計算は必要だが、AO Fieldsでやったような各ボクセルでのキューブマップレンダリングは用いず、より簡略化したアルゴリズムを用いる。

 対象オブジェクトをテクスチャにハイトマップとしてレンダリングし、その結果に対して4層分のレイヤーで区分けし、各層の各テクセルに対応するハイトマップの高さ値と、その周辺の高さ値を吟味して遮蔽率を計算する。ハイトマップをデプスバッファとして捉えれば、この事前計算でやっている処理内容は、SSAOとほぼ同じだ。

 SSAOの処理と違うのは、中空に浮いた地点に対しても遮蔽率を計算する点だ。これは、例えば窓枠をまたぐような第三者オブジェクトにも陰色を付けられるようにするためだろう。

【AO Decals その1】
AO Decalsでは、αRGBの4テクセルを、4層分の各地点の遮蔽率用に割り当てる。基本的な考え方はAO Fieldsと同じだが、AO Decalsでは、遮蔽方向は省略し、ボクセルも4層に限定している(左)。オブジェクトをハイトマップとしてレンダリング。これに対して4層を切って、各地点での遮蔽率を求める。遮蔽方向はこの図で言えば直上の半球方向に対して求める(右)

【AO Decals その2】
薄いオブジェクトといっても4層はかなり粗い空間分割となる(左)。遮蔽率は、オブジェクトの表面上の各地点から行なわれる。結果は、その各地点に1番近い層に格納される(青点)。他者への遮蔽にも対応するため中空点においても遮蔽率を計算する(緑点)(右)。

「inFAMOUS 2」におけるAO Decalsの仕様。AO Decalsではε=0.7としてAO Fieldsよりは小さめな境界箱を割り当てている

 AO Decalの大きさも、AO Fieldsのときと同じように、各オブジェクトがすっぽりと入り、なおかつやや大きめとするが、AO Fieldsの時よりも大きさは控えめとなっている。これは処理負荷への配慮と、オブジェクトの厚みがそもそも薄いので、それほど大きくしなくても事足りるためだろう。

 データサイズとしてのAO Decalsは、各辺64~128テクセル程度とし、格納内容がスカラ値の遮蔽率なので多少の誤差も許容できると言うことで、DXT5圧縮を適用している。データサイズとしては4~16KBということで、AO Fieldsと同程度。それほど大きくはない。

 ランタイムでのAO Decalsの適用は、AO Fieldsと全く同じだ。全オブジェクトのレンダリングを終えたシーンに対し、AO Decalsサイズの直方体をレンダリングして、求められた交叉箇所で、AO Decalsに格納された遮蔽率を元に陰色を付けていく。AO Decalsでは遮蔽方向が簡略化されて存在しないので、計算はAO Fieldsよりもだいぶ単純だ。

 ただ、シーンとAO Decalsの交叉箇所の深度値に対応する遮蔽率を、4層にスライスしたAO Decalsの1層から読み出してきただけではポイントサンプルとなって汚くなってしまう。なので、なんらかのテクスチャフィルタリングを導入する必要がある。しかし、単一テクセル内のαRGBの各値に対してのフィルタリング処理というのはハードウェアではできないので、自前のピクセルシェーダープログラム側で行なう必要がある。


【AO Decals その3】
遮蔽率と遮蔽強度値だけで陰色を決定(左)、AO Decalsから車へ位置を持ってくる際のフィルタリング処理のシェーダーコード例(右))



■ AO Decalsの改良

丸で囲んだ部分に白い線のアーティファクトが

 と言うわけで、ここまでの処理を適用したのが右記スクリーンショットなのだが、壁面と窓枠の交叉箇所にうっすらと白い線が見えてしまっている。実はこれもAO Fieldsで見られたような、粗いボクセル化の弊害で、境界面での急激な遮蔽率の変化が起きていることが原因だ。具体的には、境界面内側が遮蔽率ゼロで、境界面外側が“高”遮蔽率という場合に、テクスチャフィルタリングの効果によって取り出した遮蔽率が薄まってしまったことで起こる。

 これを防ぐには、ハイトマップに潜っている側の境界面の遮蔽率は、その隣側にある、ハイトマップに潜っていない境界面側の遮蔽率で上書きしてしまえばいい。これで、境界面の遮蔽率が急激ではなくなり、白い線のアーティファクトも消し去ることができる。


青点は通常の遮蔽率を求めて格納した点。赤点はハイトマップに潜った特殊点(左)、境界付近の遮蔽率は均一化してしまうことで、この付近でのテクスチャフィルタリングによる遮蔽率の急変を回避する(右)

この対処を適用する前(左)、白い線のアーティファクトが消失(右)

 上のスクリーンショットは実はちょっと結果を先走ったショットで、実はまだ、壁面に対して直交するオブシェクトの側面の陰色が柔らかくなってしまうというアーティファクトが残存している。窓の例で行くと、まるで側面が丸みを帯びているかのような陰影になってしまうのだ。

 これに関してはAO Decalsをさらに改変するのではなく、各オブジェクト側の背後に無限に広がる壁面平面があるとして、各頂点の付随情報として、この壁面からの遮蔽率を焼き込んでしまうことで対処する。つまり、壁面に直交する側面に出る濃い陰色はAO Decalsの効果ではなく、頂点への焼き込みAOの効果と言うことだ。ここまでの内容を映像にまとめたものが、下の動画となる。

【AODecals映像】
オブジェクトの周囲に出ている陰色はSSAOによるものではなく、AO Decalsによるものだ。SSAOは、この映像では意図的にキャンセルされているとのこと

AO Decals単体では、このように側面の陰色が直上にいくにつれて淡くなってしまい、あたかも丸みを帯びているかのような陰影に見えてしまう(左)。図中の式で求めた壁からの遮蔽率をオブジェクトの各頂点に焼き込んでしまうことで対処(右)

この焼き込みを導入する前(左)、この焼き込みを導入した結果。側面の陰色がくっきりと出るようになった(右)



■ おわりに~AO Fieldsの考え方を自身が変形する人体へ適用する方法

将来は、このAO Fieldsの考え方を自身が変形する人体のようなオブジェクトにも適用していくことが課題。しかも、それは決して夢物語ではない

 セッションの最後に「inFAMOUS 2」においてAO FieldsとAO Decalsを導入したことで、増加した追加テクスチャ容量のレポートが公開された。

 「inFAMOUS 2」ではこのAO FieldsやAO Decalsの概念を導入したユニークオブジェクトは119種あったそうで、それらの総テクスチャ容量(AO Fieldsの3DテクスチャとAO Decalsの2Dテクスチャの合算容量)は、わずか569KBだったととのこと。119個のオブジェクトに対する追加テクスチャ容量としてはまずまずの増加量で、これに得られる効果を考えればリーズナブルな方策だと言えそうだ。

 パフォーマンス的には、1フレーム当たり、平均的に見て20~100個のAO Fields、AO Decalsの描画負荷が掛かっているとのことだが、その所要時間はPS3で0.3ms~1.0ms、ワーストケースでも2.3msだったと分析されている。基本的には頂点負荷よりはピクセル負荷の高い処理なので、表示フレームに対して大きくこの手法による陰色が描かれる際に負荷が高くなるようだ。

 AO Fields、AO Decalsは、現在、オブジェクト自身が変形しない車などの大道具オブジェクト、ゴミ箱のような小道具オブジェクトにしか適用されていない。オブジェクトが動いても、ちゃんと、その動いた先で他者への遮蔽は配慮されるが、手足が動くような人体のような、それ自体が変形するオブジェクトに関しては、このAO Fields、AO Decalsは対応できていない。この部分が、今後取り組んでいくべき拡張の方向性だとReed氏は結んでいる。

 ただ、人体のように「自身が変形する」オブジェクトに対してのAO FieldsのアイディアはReed氏も持っており、それは、人体ならば腕、足、首、胴といった主要人体構成パーツに分解し、それぞれの部位に対してAO Fieldsを処理してやる……というアプローチだ。つまり、変形するオブジェクトを変形しないオブジェクトの集合体として処理してやるのだ。こうすることで追加負荷もリニアスケールで見繕うことができる。

 SSAOを発端にした新しいシェーディングの考え方は、今後、しばらくはまだまだ進化を見せつつ、しかも流行っていくことだろう。


AO FieldsとAO Decalsによる追加メモリ使用量(左)と追加GPU負荷(右)

Amazonで購入

(2012年 3月 14日)

[Reported by トライゼット西川善司]