読者です 読者をやめる 読者になる 読者になる

Temporal Screen Space Reflections


Temporal Screen Space Reflection

以前諦めた真面目なスクリーンスペース反射の実装に再挑戦して、今回は割といい成果を出せました。
この成果に至る過程が長く苦しくもおもしろかったので書き残しておこうと思います。

スクリーンスペース反射はその名の通り、スクリーンスペースで反射の映り込みを実現するもので、G-Buffer の法線と深度を見てレイマーチでトラバースすることで実現します。
画面内にあるものしか映せないという致命的な欠点こそあるものの、本来レイトレが必要なはずの任意面反射を破格のコストで実現することができ、Crysis2 が採用して以降、近年の大型タイトルではもはやデファクトスタンダードとなっています。


まずは素朴な実装で、G-Buffer の深度と法線から、カメラ -> pixel 位置に対する反射ベクトルを求め、深度バッファをレイマーチしてみました。
しかし、実際にやってみるとこれだけでは綺麗な結果は得られないことがわかります。


TSSR wip1

画面中央下部のような、ヘンな階段状の隙間が出てしまうのです。静止画だとわかりにくいですが、実際にカメラを動かしてみると許容しがたいレベルで気になると思います。
これはレイがオブジェクトの端を貫通してしまうために起きる現象です。レイマーチングではレイは非連続なため、レイを進める際、間に何かあっても衝突を検出できずに突き抜けてしまいます。その結果このようなアーテファクトが出てしまいます。
レイを進める間隔を極限まで小さくすれば起きなくなるはずですが、そうすると超でかいステップ数にしないと必要な距離をトラバースできず、重くて現実的ではありません。
前回諦めたのはこれにうまく対処する方法がわからなかったためでした。


こういう問題には大抵乱数を用いてごまかす対処方法が用いられるそうです。
レイマーチの際、乱数で適当に下駄を履かせます。具体的には各レイを事前に 1ステップ分の距離 * 0.0~1.0の乱数 だけあらかじめ進めておく感じです。
そうすると以下のような結果が得られます。(乱数は現在 pixel の位置をハッシュする方式)


TSSR wip2

ノイジーになりましたが、階段模様よりはマシになった気がします…が、これもカメラが動くとちらつきまくって許容できないレベルで気になります。しかし本番はここからで、これを最近流行りの temporal というやつでいい感じにします。
乱数を毎フレーム結果が変わるようにして (位置*現在の時間とかをハッシュする方式に変える)、過去のフレームの結果を累積して平均値を結果として出します。これによりノイズが平均化され、レイが貫通しにくいところはそのまま、レイが貫通しやすいところは色が薄くなります。


result

劇的な改善が見て取れると思います。実装してて感動した部分でした。
結果が安定するのに数フレーム時間がかかる (=それまでノイズが目立つ) のが難点ですが、大抵はその欠点を補って余りある効果が出せるんじゃないかと思います。
(もちろん temporal の代わりに 1 フレーム中に気が済むまでループを回すことで即座に良好な結果を得られますが、重すぎて現実的ではありません…。累計値が低いところは 1 フレームに複数回回す、のようなアダプティブ方式はありかもしれません。)

累積する一方だとカメラが移動した時残像が残ってしまうので、移動量などに応じて累積結果を減らす処理も入れます。
前フレームの深度バッファとカメラ行列を保存しておき、現在の pixel の位置を前フレームのカメラ行列でリプロジェクションして、そのスクリーン座標の前フレームの深度バッファから位置を取得し、現在 pixel 位置との差分 (=移動量) を求めます。これの大きさによって累積中の結果を適当に減衰させます。

ここまでを実装したのが今回の成果になります。
今回の例では減衰はやや弱めにしており、やや残像感が出てしまっています。その代わりカメラがゆっくり移動する時のノイズが低減されています。ここらへんは悩ましいトレードオフです…。
あと、映り込むオブジェクトは変化しても累積結果を減らしていないので、変化が映りこみに反映されるまでにラグが発生します。例えば今回の例だと光の溝の変化が映り込むまでにラグがあるのが見て取れます。たぶんそんなに気にならないだろうということで今は対処していません。


実装に際しては GPU Pro5 を参考にしました。もっとも、GPU Pro5 の記事の主題は "Hi-Z Screen-Space Cone-Traced Reflections" という、もう一歩アドバンスドな方法だったんですが、そっちはよくわからず実装には至っていません。今後の課題です。しかしこの記事はスクリーンスペース反射が基礎からフォローされてて悪くはない内容だったと思います。
また、Hi-Z ... の概要については hanecci さんが詳細にまとめられておられます

余談ですが、今回のデモの光の溝は box folding パターンというやつを用いています。最近詳細を知ったんですが、プログラマー兼アーティスト的に心強い味方となってくれそうです。
http://glslsandbox.com/e#21290.5