Spatiotemporal Variance-Guided Filtering: Real-Time Reconstruction for Path-Traced Global Illuminationを読んでみた

略してSVGFです。相変わらず私的メモ要素が強いですが…
とりあえずビデオを見てもらえばわかりますが、リアルタイムレイトレで、当面は重要な技術要素になるDenoiserの一つです。

参照

Spatiotemporal Variance-Guided Filtering: Real-Time Reconstruction for Path-Traced Global Illumination
ビデオ

SVGFに寄与する3つのComponent

SVGFには大きく分けて3つの下記の要素があります。(参照論文のFig2参照)

Temporal Accumulation

1sppを前提としているので、時間方向で、Illuminationの履歴を累積します。これにより、より多くのサンプルが結果に寄与することになります。

Spatial Filter

Temporal Accumulationが時間方向のフィルターだとすれば、こちらは空間のフィルターになります。ベースとなるのは、Edge-Avoiding À-Trous Wavelet Transform for fast Global Illumination Filtering[Dammertz 2010]です。
これのWeighting Function(Edge Stopping Function)に独自の工夫を施します。

上記2つの処理ををあわせてFig2のReconstruction Filterに相当する処理を行います。

Temporal Anti-Aliasing

こちらはReconstruction Filterとは独立しています。いわゆるTAAですが、こちらもレンダリング結果のStabilityを向上させるのに重要な役割があります。

Renderingの大まかな流れ

細かい説明は抜きにして、とりあえずの流れを確認します。
参照論文の、Fig2とFig3を参照してください。

  1. G-BufferをRasterizerでレンダリング(Depth, Albedo, World Normal,Material-ID, Motion Vector)
  2. Depth BufferからWorld PositionをReconstructionして、1 spp でパストレ。
  3. Direct LightingとIndirect Lightingの結果を別々に格納し、それぞれに後述の処理を行う。
  4. G-BufferのAlbedoをパストレ結果からDemodulateしてUntextured Illumination Componentを算出
  5. Temporal Accumulation(後述)をかけて Integrated Color と Integrated Color Momentを算出(Local Luminance Variance(後述)の計算で使う。)
  6. Local Luminance Varianceを計算(A-Trous Wavelet TransformのWeight Functionで使う)
  7. A-Trous Wavelet TransformをIteration する。(後述)
  8. Filterされた結果に、G-BufferのAlbedoをModulateする
  9. Tone Mapping(SVGFには関係ない)
  10. Temporal Anti Aliasing.(通常のTAA)

Temporal Accumulation (4.1章参照)

パストレの結果が、1sppでは根本的に情報不足なので、過去のフレームのレンダリングより、なるべく情報を補完します。
G-Bufferの2D motion vector から前フレームのピクセル位置にback projection して、Color History Buffer(このバッファの内容については後述)をサンプリングしてAccumulation を行います。この際にG-bufferのdepth, object space normal, mesh IDを比較して、同一Surfaceかをチェックします。異なれば、Color History Bufferのサンプルを棄却します。同一サーフェースと見なせる場合は、Exponential moving averageでAccumulationします。
動いている物体における品質を上げるためColor History Bufferのサンプルを2×2のカーネルで行い、サンプルの平均化と棄却を行います。2×2のカーネルから有効なサンプルが拾えない場合は、3×3にフィルターを拡大します。さらに失敗する場合はaccumulation自体を行いません。

Variance Estimation(4.2章参照)

後段のA-Trous wavelet transformで使用する、LuminanceのVarianceを計算します。これは、Reconstruction Filterは、ノイズのない領域や、少ない領域(例えばFully LitとかFully Shadowedの領域)のサンプルをあまり変更するべきではない一方で、Noisyな領域では、広い範囲でFilteringを行うべきだからです。ColorのTemporal Accumulationと同様にLuminanceとLuminanceの2乗が格納されたMoment History Bufferを用意し、Temporal Accumulationを行います。そこから単純に分散を計算すれば、特定のピクセルにおける、時間方向のVarianceが計算できます。
ただし、算出されるVarianceは、不完全なノイズのプロキシでしかありません。ノイズはVarianceを増加させますが、Varianceはノイズがない状態でも増加します。Disocclusion(新しい物体がレンダリングされた場合)はこのVariance Estimationの品質に悪い影響を与えます。したがってDisocclusionから4フレーム以内は、7×7のBilateral Filter(DepthとWorld NormalでWeighting)を使って、周辺のPixelからVarianceを取得します。

Edge-avoiding a-trous wavelet transform(4.3章)

もとになったのはこちら。基本的に下記論文の内容に沿ったものになっています。
Edge-Avoiding À-Trous Wavelet Transform for fast Global Illumination Filtering
A-Trous wavelet transformは、階層的にWavelet変換を繰り返し、Waveletの係数を算出します。Iterationをするたびに、Supportのサイズを倍にますが、非ゼロの要素の数は変えません。言い換えれば、Iterationするたびに、より疎で広範囲なサンプリングをします。Wavelet変換後に係数をReconstructionすれば元の画像が復元できますが、画像のFilterとして、これを利用するので、最終階層の結果を出力とします。
Edge Stopping Function(後述)が、過度に画像が平滑化されないようにして、画像のディティールを保ちます。A-Trous wavelet transformのIterationは5 Stepで、各Stepで5×5のCross Bilateral FilterにWeight Functionを適用して計算します。最終的には65×65のPixel Foot Printになります。(5->9->17->33->65)
Filterの係数は単純なB3スプラインで、[Dammertz 2010]と同じです。ラスタライズされたG-Bufferと上記で計算したVarianceを基に、Edge Stopping Functionを計算します。
Edge Stopping Functionは、結局のところ、Cross Bilateral FilterのWeight Functionと同義です。参照論文では3つのWeight Functionを定義して、その乗算結果をFilterのWeightとして使用しています。
G-BufferのDepthを用いて算出されるWeightと、World Space Normalを基に算出されるWeightに加え、Luminanceと、先に算出した、LuminanceのVarianceを用いて計算するWeightがあります。
これらを用いて、A-Trous wavelet transformをTemporal AccumulationしたColorに適用します。
さらに、Filterの係数とWeightの乗算結果の分散を計算し、LuminanceのVarianceと乗算して、LuminanceのVarianceを更新し、次の階層で使用するLuminanceのVarianceとして用います。
また、Wavelet Transformの最初のStepの結果を、Color History Buffer(Temporal Accumulationで既出)として、次のフレームで使用します。

Edge Stopping Functions(4.4章)

3つのEdge Stopping Functionがあり、それぞれが、Filterの各サンプルの寄与度を決定する要素として作用します。

Depth : 参照論文4.4章eq(3)参照

DepthはLocal Linearモデルを用いて計算します。基準点のScreen SpaceのPartial Derivativeと、サンプル点までの距離を乗算したものと、Depthの差との比を基に計算し、exp()関数でWeightを付けます。GradientよりもDepthの差が大きくなれば、このWeightは小さくなり、サンプルは棄却される傾向となります。

Normal: 参照論文4.4章eq(4)参照

サンプル点と基準点の法線の内積を128乗したものをWegihtとします。ベクトルの向きが異なり、内積が小さくなれば、サンプルが棄却される傾向となります。

Luminance: 参照論文4.4章eq(5)参照

基本的には、LuminanceのVarianceから算出された標準偏差を基準に、基準点とサンプル点のLuminanceの差と標準偏差との比を計算して、exp()関数を適用したものです。Varianceが小さい場合は、よりLuminanceの差に対して敏感に反応し、サンプルが棄却される傾向があります。
ただし、サンプル数が少なく、Varianceの値が安定しない領域が、実際のレンダリングにはあるので、事前に3×3のGaussianFilterをVarianceに適用しています。
FilterのIterationが進むにつれ、前の章で説明した通り、Varianceの更新を行うため、Varianceが減少する傾向になると思われます。さらに、他のEdge Stopping Functionによって、Weightの値が小さくなった場合は、さらにVarianceが減少する傾向が強まると思われるので、そういった個所では、Iterationを繰り返すと、よりLuminanceの差に敏感に反応するようになると思われます。これによってOver Blurringを抑制します。

まとめ

このFilterの面白いところは、LuminaceのVarianceを計算してFilterに適用している所だと思います。ただ、Tone Mapping前のLuminanceなので、レンダリングされた内容によっては非常にVarianceが大きくなる可能性があるのではないかと思います。逆に、暗いシーンでは、Varianceが実際のレンダリングのノイズに対して小さくなる傾向があると思います。Luminanceを事前に正規化(あるいはそれに近い形で、画一的にに扱えるようにするための処理)する必要があるかもしれません。
だた、参照のビデオを見てもわかる通り、このDenoiserの効力は大きいと思います。また、それと同時に、当面のリアルタイムレイトレーシングの技術力の差は、これらのFilterをいかに上手く適用するかにかかっていると思います。

広告