2013/05/20

SSAO に再挑戦していた

数年前に SSAO (Screen Space Ambient Occlusion) を実装しましたが、自身の実装と解釈に誤りがあると認識しつつも放置したので、SharpDX ベースの自作フレームワークに入れるために、再度、納得できる質まで改善していました。

数年前に参考にした実装は下記サイトであり、今回もこれをベースにしました。
Game Rendering - SSAO: http://www.gamerendering.com/2009/01/14/ssao/
大まかな処理の流れは、Ray Marching Ambient Occlusion を基礎とし、法線による平面間に対する判定を除去しているのだと思います (実装上は法線差による閉塞度に対する重み付けの調整)。

今回は、上記サイトの実装をベースにしつつ、深度を線形深度マップ (Linear depth map) へ置き換え、線形深度からビュー空間座標を再構築して判定するような実装を組んでいました。その他、シェーダ内でハードコードされた球面内サンプル点や、テクスチャ ファイルとして用意しているランダム法線マップをなどを C# 側で生成してシェーダへ渡したりしています。

相変わらず、テストに使用しているシーンが簡素過ぎますが、以下、ひとまずは納得できた結果の画像です。
ブラー前 SSAO マップ
ブラー後 SSAO マップ
SSAO 適用シーン
レイによるサンプル数を 8 として SSAO マップを生成しています。生成された SSAO マップへのブラーでは、バイラテラル フィルタの変形バージョンを 3 回適用しています。1 回の適用でもそれなりですが、3 回程適用すると十分な綺麗さが出る感じがします。なお、単純に色の差異でバイラテラル フィルタを適用すると期待するブラーにはならないので、深度マップを参照して深度の差異で重みを変化させています。加えて、法線の差異でも重み付けを変化させているため、厳密にはバイラテラルではないのだろうと思います。法線を考慮せずとも十分なブラーを得られますが、法線差異を制御するための標準偏差を色々と変化させてみると微妙な調整ができるので、これで良いとしています。

負荷を下げるために SSAO マップのサイズを小さくし、シーンと合成する前にアップサンプリングしてみたりなどもしましたが、単純なアップサンプリングではシーンとの合成時に解像度の差異から汚くなる部分が出てくるため、少し考慮が必要なのだろうと感じています。
追記: と思っていたら、どうも僕のダウンサンプリング実装に誤りがあり、修正して適用してみると、悪くない結果でした。3D グラフィックスは、僅かな値の差ではそれなりに稼働してしまうので、デバッグが怖いですね。
何にせよ、納得できる所まで持ってこれて、心底安心できました。

2013/05/12

長らく Bordelands 2 プレイ中

積んでいた Borderlands 2 を 2 月頃からやっているのですが、未だに飽きません。始めた頃は、初代と違いがないように感じたので、早々に飽きるだろうと思いながら進めていたのですが、気付いたらハマっていました。COOP はやらず、常時ソロです。

ゲイジで三周目をクリアし、今はマヤで三周目保護区をクリアした所です。三度、無策で挑み、あっさりと雑魚に追い込まれて絶望したので、用いた攻略方法を記載しておきます。

検索してみると、フリート (スキル) と The Rough Rider (シールド) で攻略した人を見つけることができ、真似てみたら楽に攻略できました。フリートと The Rough Rider の組み合わせで常に移動速度上昇状態にし、高速移動により回避を楽にして戦うという方法です (僕はクラス MOD 含めてフリート 10 でやりました)。

高速移動により、雑魚や地上に降りたブラッドウィングを簡単に振りきれるので、ブラッドウィングの急降下の回避に集中する感じです。急降下回避に自信が無い場合は、適度に雑魚のライフを削っておき、フェーズロックでの回復やセカンドウィンドに備えておけば安全かもしれません。

2013/05/10

Volumetric Light Scattering に再挑戦していた

数年前に公開したブロック地形動画では Volumetric Light Scattering (God Ray、Crepuscular Ray) を実装していましたが、当時はまだ理解できていなかった部分があったため、再実装していました。あれから新たについた知識を元に実装すれば、また新たな側面が見えてくるであろうと考えて。

Volumetric Light Scattering については以下に説明があります。
NVIDIA GPU Gems 3 - Chapter 13. Volumetric Light Scattering as a Post-Process
http://http.developer.nvidia.com/GPUGems3/gpugems3_ch13.html
Volumetric Light Scattering は日本語で何と言うのでしょうか。端的には光芒だとは思いますが。Scatter は散乱なのでしょうか。特に、Volumetric の適切な訳がいつも分かりません。

今回の実装では、ライト閉塞マップを作成し、ライト閉塞マップに対して Volumetric Light Scattering シェーダを適用してライト散乱マップを作成し、ライト散乱マップと通常シーンを加算混合するという手順でシーンを生成しています。
Volumetric Light Scattering 効果なし
Volumetric Light Scattering 効果あり
スカイ スフィアを描画し (方向性光源の方向が示す箇所に太陽をシェーダで描画)、後は立方体を適用に並べています。ライト閉塞マップやライト散乱マップを黒色をベースに生成していますが、これは最後に通常シーンと加算混合するためです。

シーンの選択に問題がある気はしますが、一応、実装は上手くいったようであると感じています。

今回は、SharpDX での自作フレームワーク上で実装しているので、ソースは公開しません。と言っても、GitHub 上でリポジトリが公開状態ではありますが (Libra リポジトリに含まれています)。自作フレームワーク上の実装では SharpDX としてのサンプルにもならないですし、XNA へまた移植するのも疲れるしなぁと。

XNA 上でのサンプルとしては、下記サイトが良いと思います (ソースコードも公開されています)。
XNA-UK: Randomchaos Archivs - 2D Crepuscular (God) Rays
http://xnauk-randomchaosblogarchive.blogspot.co.uk/2012/10/2d-crepuscular-god-rays.html
上記サイトでは 2D アプリケーションとして実装していますが、Volumetric Light Scattering シェーダはテクスチャに対して適用するので、3D でも同じ事です。

他には、ライト閉塞マップに対して Radial Blur を適用してライト放射マップとし、シーンへ混合する方法もあるようです。そもそも、Volumetric Light Scattering シェーダは、ライト関連の調整を行いやすいように Radial Blur を変更した物と見做すこともできるかもしれません。

2013/05/06

XNA PSSM

SharpDX ベースの自作フレームワークで PSSM (Parallel-Split Shadow Maps) 関連のコードを整理したので、これを XNA 実装として公開しておきます。
PSSMDemo
https://github.com/willcraftia/TestXna/tree/20130506/PSSMDemo
ZIP DL はリポジトリから
https://github.com/willcraftia/TestXna/tree/20130506
自作フレームワークからの簡易移植であり、XNA としてコードを綺麗に整理していません。また、TestXna リポジトリには PSSMDemo プロジェクト以外も含まれ、リポジトリの ZIP ダウンロードには関係の無いプロジェクトも含まれます。

PSSMDemo は、先に公開した LiSPSMDemo 同様に、XNA Shadow Mapping サンプルを原型として実装してあります。今回もライトは方向性光源のみです。また、自作フレームワーク側で色々とリファクタリングを行ったので、クラス構造が LiSPSMDemo とは大きく異なります。

なお、ビルドに必要な XNA コンテンツは Git 管理外としているため、別途、XNA 公式サイトより XNA Shadow Mapping をダウンロードし、XNA コンテンツを LiSPSMDemo へコピーしてプロパティを設定する必要があります。
XBOX LIVE indie games - シャドウ シリーズ 1: 基本シャドウ マッピング
http://xbox.create.msdn.com/ja-JP/education/catalog/sample/shadow_mapping_1
PSSM ですが、日本語で纏められたサイトを見つける事ができませんでした。NVIDIA の GPU Gems を参考にしてください。
GPU Gems 3 - Chapter 10. Parallel-Split Shadow Maps on Programmable GPUs
http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html
MS の DirectX サンプルなどでは、カスケード シャドウ マップと紹介されています。
簡単に説明すると、カメラの視錐台を分割し、分割された視錐台それぞれについてシャドウ マップを生成し、影付きシーンを描画する際には、描画するオブジェクトの位置から適切なシャドウ マップを選択し、そこから深度を選択するという処理です。
分割された視錐台に対するシャドウ マップの生成では、XNA Shadow Mapping にあるような基礎的なロジックを用いても良いですし、LiSPSM を用いても良いです。
重要な点は、視錐台の分割により、一つのシャドウ マップで対象とするシーンの領域を小さくし、シャドウ マップの品質を向上させる点です。
以下、シャドウ マップを Basic (R 値に深度を書き込んだだけのもの) とし、ライト カメラに LiSPSMLightCameraBuilder (LiSPSM)、UniformLightCameraBuilder (USM)、BasicLightCameraBuilder (Basic ライト: XNA Shadow Mapping とほぼ同じロジック) を用いた比較画像です。いずれも、分割数 3、シャドウ マップ サイズ 2048x2048 (XNA Shadow Mapping と同サイズ) です。
分割3 + LiSPSM ライト + Basic マップ (2048x2048)
分割 3 + USM ライト + Basic マップ (2048x2048)
分割 3 + Basic ライト + Basic マップ (2048x2048)
このように、視錐台分割により、Basic ライトであっても、それなりに綺麗な影を生成できることが分かると思います。

分割数を少なくする程に、分割無しの品質に近づきますが、LiSPSM や USM は分割無しでもそれなりの品質であるため (元より分割無しでの品質向上アルゴリズム)、Basic ライトの場合にのみ大きな影響が表れます。
以下、Basic ライトについての、分割数を変更した場合の比較画像です。
分割 3 + Basic ライト + Basic マップ (2048x2048)
分割 2 + Basic ライト + Basic マップ (2048x2048)
分割 1 + Basic ライト + Basic マップ (2048x2048)
分割 1 は、XNA Shadow Mapping と概ね同等のシャドウ マップ生成です。

以下、Basic ライトについてシャドウ マップ サイズの変化による比較画像です。
分割 3 + Basic ライト + Basic マップ (2048x2048)
分割 3 + Basic ライト + Basic マップ (1024x1024)
分割 3 + Basic ライト + Basic マップ (512x512)
1024x1024 でも、それなりに綺麗かなと僕は感じます。512x512 は流石に厳しいですかね・・・。Basic ライトに加えて VSM を適用すると、更に綺麗な感じにはなりますが、ブラー適用の度合いにより色々と変化するので、比較画像は省略します。なお、VSM を用いる場合、分割数分だけブラーを適用する (負荷が増大する) ことにもなるので、適用すべきか否かについて十分な検討が必要であろうと思われます。例えば、2048x2048 のシャドウ マップに対して VSM のために 3 回のブラーを適用すると、ブラーのカーネル次第ですが、負荷も相当なものになります。

以下、おまけのような物ですが、サンプルでは影の色を C# コード上で変更できるようにしているため、(0, 0.5, 0) に設定した場合の例です。
影への色付け
影を緑色にしたい人は稀だとは思いますが、状況によって影の濃さは異なると思うので、黒をベースにその度合を変えて利用するなども良いかなと感じます。

まぁ、こんな所です。いつまでもシャドウ マッピングに拘っていてはいけないと感じていて、雑に XNA へ移植しています。おかしい点があれば、ご指摘して頂けると幸いです。

2013/04/29

XNA LiSPSM (および VSM)

XNA での LiSPSM (Light Space Perspective Shadow Maps) 実装のサンプルを公開しておきます。
LiSPSMDemo
https://github.com/willcraftia/TestXna/tree/20130429/LiSPSMDemo
ZIP でのダウンロードは以下から (関係ない物も含まれてしまいますが)
https://github.com/willcraftia/TestXna/tree/20130429
XNA Shadow Mapping を原型として LiSPSM の機能を乗せ、それらを切り替えて確認できるようにしてあります。ただし、ライトとしては方向性光源のみを実装し、点光源には対応していません。
なお、ビルドに必要な XNA コンテンツは Git 管理外としているため、別途、XNA 公式サイトより XNA Shadow Mapping をダウンロードし、XNA コンテンツを LiSPSMDemo へコピーしてプロパティを設定する必要があります。
XBOX LIVE indie games - シャドウ シリーズ 1: 基本シャドウ マッピング
http://xbox.create.msdn.com/ja-JP/education/catalog/sample/shadow_mapping_1
LiSPSM 公式サイトにある C++ コード (オリジナル コード) をベースにしていますが、オリジナル コードのままでは問題が幾らかあるため、Ogre の LiSPSM 実装を加えて参考にしています。なお、Ogre では LiSPSMShadowCameraSetup が LiSPSM の実装部分であり、その原型はオリジナル コードです。
Light Space Perspective Shadow Maps
http://www.cg.tuwien.ac.at/research/vr/lispsm/
Ogre
http://www.ogre3d.org/
また、日本語での LiSPSM の解説については Project ASURA を参考にしています。ここでは LiSPSM の仕組みについては述べないため、詳しく知りたい方は Project ASURA の解説を参照すると良いでしょう。
Project ASURA - Light Space Perspective Shadow Maps
http://asura.iaigiri.com/OpenGL/gl59.html
なお、VSM  (Variance Shadow Maps) も合わせて乗せています。
Variance Shadow Maps
http://www.punkuser.net/vsm/
以下、シャドウ マップを VSM とし、LiSPSMLightCamera、FocusedLightCamera、BasicCamera による各ライト カメラでの比較画像です。いずれも、XNA Shadow Mapping で用いられるシャドウ マップ サイズ 2048x2048 です。

LiSPSM + VSM
Focused + VSM
Basic + VSM
LiSPSMLightCamera は、LiSPSM 実装そのものです。FocusedLightCamera は、LiSPSM で用いられる視錐台の歪みに相当する行列を単位行列としたものです。Basic は XNA Shadow Mapping で用いられる基礎的なライト カメラです (実際にはそこから更に不要と判断したコードを除外しています)。

カメラとライトが平行になる状況における補正

カメラとライトが平行に近づくと LiSPSM は大きく品質が劣化しますが、LiSPSMDemo ではそのような場合に n 値に補正を掛け、品質を向上させています。これは、Ogre で実装されている仕組みを参考に、オリジナルの n 算出式の結果に対して適用しています。なお、Ogre では n 算出式が完全自動ではなく、プロパティによる調整を前提に作られているようです (描画するシーンに応じてプロパティを適切に調整する必要があると思います)。

以下、補正 ON と OFF での比較画像です。差異を顕著にするため、VSM ではなく Basic でシャドウ マップを生成しています。
LiSPSM + Basic + 補正 ON
LiSPSM + Basic + 補正 OFF
ここでの補正の仕組みは、カメラとライトの内積の絶対値が、指定の閾値を越える場合に並行であると見做し、並行と見做せる場合に n 値を若干大きくしてしまうという物です。カメラとライトが並行になる場合に発生する品質の劣化は、n が 0 に近づくことで発生するため、0 に近づけさせないようにして解決するという事です。

凸体 B の押し出しに関する問題

方向性光源を用いる場合、凸体 B を構成する際には、表示カメラの視錐台をシーン領域でクリップした後、B の各頂点からライト方向へレイを飛ばし、幾らか B の範囲をライトのある方向へ押し出して領域を拡張する必要があります。
これを行わない場合、投影オブジェクトがカメラの外に出るような状況において、その分だけ影が描画されない問題が発生します。この問題は、例えば、このサンプルにおいては、デュード モデルの影に非常に接近する場合に起こりえます。
オリジナル コードでは、そのようなレイとシーン領域との交点を B へ追加しますが、どうやらレイの始点となる各頂点がシーン領域の縁に存在し、結果として始点が交点となってしまい、交点の意味が無いという状態になってしまいました。もしかすると、凸体 B のクリッピングに誤りがあるのかもしれません。

これに対し、Ogre では、そのようなレイにそって一定の距離 (ライトの遠平面距離) にある点を追加して解決しています。そこで、LiSPSMDemo においても同様の概念で凸体 B を押し出しています。

ただ、実際にはどうなんでしょう?
表示カメラの視錐台をシーン領域でクリップする処理は、シーン領域の一部が視錐台によりクリップされる事も含んでいます。つまり、視錐台の外にある投影オブジェクトは凸体 B の外に出てしまうので、描画範囲から除外されてしまいます。そこで、このような投影オブジェクトを描画範囲に含めるため、凸体 B をライトのある方向へ少し押し出し、最終的なクリッピング領域を広げているのだと思います。ただ、これ自体は、LiSPSM に限らず、一般的にシャドウ マッピングにおいて考えなければならないクリッピング領域の問題であろうかと思います。
最小限必要となる凸体 B の構築を考えた場合、投影オブジェクトで構成される領域を別途管理し、凸体 B へこれを含めるなどの処理を行うべきなのかもしれません。実際、Ogre では投影オブジェクト領域と非投影オブジェクト領域を別途管理し、それらを考慮した領域の結合などを行なっているようです。
ですが、ある範囲に対して投影しうるオブジェクトであるか否かの判定自体が面倒でもあるので、厳密な判定が不要であるならば、一定距離で範囲を広げてしまえば良い気もします。

その他思うこと

Basic でシャドウ マップを作ると、セルフ シャドウが汚いなぁと。VSM でシャドウ マップを作ると、セルフ シャドウは綺麗になるものの、VSM ではモデルと影の関係が変になるんだよなぁと。
LiSPSM + Basic におけるセルフ シャドウ
LiSPSM + VSM におけるセルフ シャドウ
LiSPSM + Basic におけるモデルの足元
LiSPSM + VSM におけるモデルの足元
まぁ、こんな所です。


2013/04/28

LiSPSM にリベンジしていた

数年前、LiSPSM (Light Space Perspective Shadow Maps) の実装を試みたものの、どうにもおかしな振る舞いとなる部分を直せずに利用を断念していましたが、納得していなかったので再挑戦していました。

LiSPSM 公式サイトでは C コードと C++ コードをサンプルとして公開しており、過去の挑戦では C コードを参考にしていましたが、今回は C++ コードを元に実装してみました。実際には、Ogre (オープンソースの 3D グラフィックス エンジン) も参考にしています。Ogre では、LiSPSMShadowCameraSetup クラスが LiSPSM のロジックにあたり、コードの基本はオリジナルと同じです。

で、方向性光源 (directional light) についてのみの実装ですが、何とかまともに動くようになりました。基本はオリジナル コードの模倣ですが、どうもオリジナルのままではカメラを光源の方向へ向けた場合に描画が上手くいかないため、Ogre のコードを真似ました。後は、Ogre では、カメラとライトが概ね平行になる場合に N の算出に補正を掛けていたので、これは良いと思い、形を変えて真似たという所です。

まぁ、自分で納得するか否かの問題で再挑戦しただけであり、恐らく LiSPSM は使わないのですが。僕は PSSM (Parallel Split Shadow Maps) を使う前提としているため、この場合には LiSPSM を用いる必要がそれ程には無いと言いますか。

いずれにせよ、LiSPSM のサンプル コードが少ないことで難儀したので、整理して公開しようと考えています。今の実装は自作 SharpDX ラッパー上の実装であり、サンプル公開として適さないため、XNA コードとして再実装しようかなと。ただ、凸体の構築ロジックではバカスカと新規オブジェクトを生成しているので、これを何とかしてからですかねぇ・・・。

2013/04/01

XNA UI Framework 動画

開発で使用するために作成した UI Framework のデモをニコニコ動画にアップしました。

 

libgdx いじり

Google が提供している Java 版の Tango Examples は Rajawali をベースにしているため、自分が仕事で開発する Tango アプリも Rajawali ベースとしていましたが、最近は libGDX への移行を進めています。一応、要点については移行が...