2013/05/30

ポストプロセスでフォグ

水面シェーダにフォグ処理を追加しておこうと試み始めたのですが、それをやり始めると、モデル描画に使用する他の様々なシェーダにおいてもフォグ処理を記述しなければならない状況に陥ります。これは嫌だなぁと。フォグの ON/OFF なども考慮すると、更にコードが汚くなるわけで。

そこで、ポストプロセスによるフォグを実装していました。検索してみると「今時はフォグもポストプロセスですよ」などと書いている海外の人もいましたが、素人の僕には実際がどうなのかは分かりません。

ボリューム フォグは既に実装していたので、線形フォグ、指数フォグ、高さフォグを実装していました。基本は、ポストプロセスのピクセル シェーダにて線形深度マップを参照し、深度からビュー空間における位置を再構築して利用する流れです。ただし、高さフォグでは、ワールド空間における y 値が必要となるため、更に逆ビュー行列を掛けて情報を得ています。

ポストプロセスならば簡単に処理を ON/OFF できるので、各フォグを組み合わせて遊んでみましたが、組み合わせる事により意外な味が出るものですね。

2013/05/29

水面描画、記念撮影。

水面描画への再挑戦ですが、何とか想定した実装になった所です。以下、記念撮影です。


一応、テクスチャ座標を時間でずらして水の流れを起こしていますが、静止画では分かるわけがないですね。
法線マップを手作業で作成することは嫌いであるため、Perlin noise  で生成しています。実装前にはノイズ設定が難しいであろうと想像していたのですが、単純に SumFractal で合成しただけでそれらしくなりました。
ノイズからの法線マップの作成は、ノイズで波を表すハイトマップを作成し、ハイトマップから勾配を算出して法線を構築するという手順です。なお、作成した法線マップはシームレス テクスチャとして自動生成しているので、アドレス モードを Wrap にして貼り付けています。


上図は少し角度を付けてみた状態です。水面は任意の面に描画できるようにしています。
波の影響を大きくしているため見辛いですが、屈折と反射を実装しています。反射の度合いには、Schlick の近似によるフレネルの式 を用いています。
ネット上では、フレネルの式から得られる値を係数として、反射と屈折を線形補間した値を水面の色としているサンプルを多く見かけましたが、僕は反射にのみ適用しています。屈折に関しては、視点から一定の距離が離れる程に、および、視線が水面に並行になる程に減衰するような仕組みにしています。この基本形は、GPU Gems 2 - Chapter 19. Generic Refraction Simulation に従っています。


水面を垂直にしてみると上図のような感じです。だから何だという話ですが。


上図は、書籍『ゲーム エフェクト マニアックス』に掲載されていた波生成の実装です。これは、アプリケーションからテクスチャ上に波紋を発生させて法線マップを作る実装です。JoJo ファンにはたまらない実装ですね。

と、何とか形になったなぁと。

ただ、テストをしていると定期的かつ瞬間的に描画がカクつく現象が起こっていて、フレームワーク側の問題であることが明らかであり、厄介な事になってきたなぁと感じている所です。実際にはとうに気付いていたんですが、見ないふりをしていた所があり、このまま見ないふりを続けるかもしれません。

どうにもならなければ、XNA に戻るだけです。

2013/05/27

水面描画への再挑戦

過去のブロック地形動画の際に、海や湖を表現するために水面のエフェクトを組んでいたのですが、これを汎用化して自作 SharpDX フレームワークに載せようと試みていました。基本的には、波を表すための法線マップを事前にテクスチャとして用意するという方法が嫌いなため、自動生成して利用する枠組みにしています。

過去の実装から思い出していたのですが、元は反射と屈折を考慮した水面を実装していたにも関わらず、何かの理由で反射と屈折を止めた記憶があるなぁ・・・と。

屈折効果に用いる水中シーンを描画するために、GraphicsDevice.ClipPlanes の設定により水面より上をクリップするという XNA のチュートリアル サイトの情報があり、過去の自分はこれを参考に実装したと思われますが、この機能は XNA 4.0 で失われています。恐らく、僕は移行方法が分からずに諦めたのではないかと。

XNA 4.0 における対応としては、クリップ面をシェーダへ渡し、頂点からクリップ面までの距離を算出し、clip 関数を用いて距離が 0 以下 (頂点がクリップ面の裏側) ならばピクセル シェーダの出力を抑制してしまう方法かと思います。
シェーダ モデル 4.0 以上ならば、算出した距離を SV_ClipDistance セマンティクスでピクセル シェーダへ渡せば自動的にクリップしてもらえるようなので、自作フレームワークではこれを利用しました。

他には、頂点シェーダの属性として [clipplanes] でクリップ面を指定すると、SV_ClipDistance に関わるコードを書かずに済むらしいのですが、これは使わない事にしました。僕は、シェーダのコンパイル時警告をエラーとして扱っていますが、clipplanes を指定するとクリッピングに関する頂点シェーダ出力の初期化が完全ではないとの警告が出てしまい、これを解決する術が分からずに断念しました。どうにも、clipplanes 属性の利用に関する情報が少なすぎて。

ひとまず、一般的な水の表現は実装できましたが、水が XZ 平面上にある必要もないと思い、自由に回転させた平面に水面エフェクトを適用しようと試みている所です。

とまぁ、気付いたらまた壮大に脱線し始めたと思うものの、どこかの時点で自身の知識の整理はすべきであり、なおかつ、更に一歩進むべきとの思いもあり。

2013/05/25

新生 FF 14 ベンチマークを実行してみた

友達から「FF 14 の発売日が決まったよ。忘れずに買えよ。」とのメールがまわって来たので、新生 FF14 の状況を調べてまわり、ベンチマークが公開されていたので実行してみました。
FF XIV: 新生エオルゼア ベンチマーク ワールド編
結果:
標準品質プリセット - 125 fps
最高品質プリセット - 60 fps 
PC は、GeForce GTX 560、i7-2600 CPU 3.40GHz、メモリ 8 GB です。

最高品質は確かに綺麗でしたが、標準品質で十分と感じました。標準品質ならば、PC には相当余力があると言えますね。

一応、FF 11 は初期組で解約と再開を繰り返して数年、旧 FF14 は半月程プレイしましたが、雰囲気について、新生 FF 14 は FF 11 の正統進化という印象を受けました。この感じは好きです。

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 へ移植しています。おかしい点があれば、ご指摘して頂けると幸いです。

libgdx いじり

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