Aggregate G-Buffer Anti-Aliasing

一応表題が、Anti Aliasingとなっており、略すとAGAAですが、AA技法というよりは、新しいDeferred MSAA技法のひとつと考えたほうが良いかもしれません。単純に他のAA技法とは比較できませんが、目指すところはAAです。

[Aggregate G-Buffer Anti-Aliasing]
http://graphics.cs.williams.edu/papers/AggregateI3D15/

おおまかな手順は以下の通りです

  1. Dense Visibility Sampling
    ここでは、ジオメトリを描画します。Depth+Normalのpre-passに相当します。MSAAのRenderTargetに描画します。
    情報の格納は(Pixelではなく)Sampleごとに行います。ただし、NormalMapのサンプリングは行わず、ジオメトリのNormalを描画します。
  2. Aggregate Assignment
    手順1で描画したDepthとNormalの情報より、事前に決めたグループ数(Aggregate数)に、各Pixel内でSampleをAggregateします。要するに、Pixel内の似通ったNormalとDepthを持ったSample同士でグループを作ります。
  3. Aggregate G-buffer generation
    ここで2-pass目のジオメトリ描画を行います。通常のDeferred Shadingでは、G-Buffer Fillingに相当します。本手法でもG-Bufferに情報を格納するのですが、Rasterizationはpre-passと同じSample単位で行い、G-Buffer情報の格納はAggregate単位で行います。G-Bufferには、先に決めておいたAggregateの数に応じたG-Bufferの格納領域を用意しておき、手順2で決めた各Sampleの属するAggregateに、G-Bufferの情報を蓄積していきます。
  4. Aggregate deferred shading
    通常のDeferred ShadingでいうところのDeferred Shading Passになります。G-BufferにはPixelごとに最大でAggregate数分の情報があるので、これらをShadingし、最終的なPixelの結果とします。
  5. Aggregateの決め方

    Pixel内のSampleのDepthとNormalの平均値を算出し、これより最も離れたパラメーターを持ったSampleを、最初のAggregateの代表値とします。次に、最初のAggregateの代表となったSampleより最も遠いパラメーターを持ったSampleを、次のAggregateの代表値とします。以下は、既に存在するAggregateの代表値より、最も遠いSampleが次のAggregateの代表値となることを繰り返し、規定のAggregate数になるまで行います。残ったSampleは、いずれかのAggregateに属するため、最も近いパラメータを持ったAggregateに追加されます。

    Aggregate G-Bufferのレンダリング

    G-Bufferの各パラメーターは、場合によっては各Sampleごとに算出され、それぞれの属するAggregateのG-Bufferに値を貢献させていきます。この処理は、言い換えればMipMapのFilteringのような処理となる為、単純な加算と平均になるとは限りません。各Attributeの特性にあった方法で値を計算します。ただし、G-Bufferの格納領域はAggregate数分しかなく、全てのSampleの値を個別に保持して、後で計算できるわけでは無いので、計算の方法には制限があります。従って、必要があればG-Bufferを拡張してFilteringの計算用の値を格納します。

    Deferred Shading

    ここは通常のDeferred Shadingに良く似てますが、ShadingはPixel単位でもSample単位でもなく、Aggregate単位で行うのが大きな違いです。さらに、AggregateごとのShading結果は、各Aggregateに属しているSample数による重み付けを行い、最終的なPixelの値とします。

    半透明

    半透明に関しては、通常のDeferred Shadingと同様の制限があります。

    G-Bufferのレンダリング

     従来のGPUでは、Rasterizerが描画するSample数と、Render Targetが保持するSample数が必ず一致している必要がありましたが、この機能[NV framebuffer mixed samples]が使えるGPUでは、これ等のSample数が一致している必要がありません。本手法では、Aggregate G-Bufferのレンダリング時に、RasterizerとDepthStencilのサンプル数をpre-passを描画した場合のSample数に設定しつつ、ColorRenderTargetをAggregateの数のSample数に設定し、レンダリングを行います。
     値を書き出すAggregateの選択は、SampleMaskをPixelShader内で書き換えることが出来るようになる、この機能[NV sample mask override coverage]を用いて実現している様です。さらに、1Sampleあたり1PixelShaderを厳守するため、Stencilを使っています。(Depthの値が全く同一の場合は、複数のPixelがDepthTestingをPassする可能性があるため)
     本手法では、PixelShaderはPixel単位でInvokeしているようです。従って、DepthStencilテストをパスしたSampleのMaskが必要になります。これは、この機能[EXT post depth coverage]でPixelShaderの入力として取得することが出来ます。DepthStencilをパスしたSampleのMaskから、SampleIDを抽出し、属するAggregateのIDを調べます。また、書き出すG-Bufferの値は、被覆するSample数に応じてスケーリングする必要があります。これは、先ほどのSampleMaskのBit数より求めることが出来ます。最後に、Pixelを書き出す際は、属するAggregateに値が書かれるようにSampleMaskを設定します。懸念として考えられるのは、同一のPrimitiveのSampleが異なるAggregateに属した場合は、上記の手法は破綻します。複数の属するAggregateを抽出し、出力のSampleMaskに正しく設定しても、Sampleの被覆数に応じたスケーリングが不可能です。従って、Aggregate形成時のThresholdは注意深く設定する必要があると思われます。

    考察

     論文では、8xMSAAに相当する品質を、33%少ないメモリで、1.3倍の性能を実現したことが書かれていますが、そもそもこの手法は別の部分に優位性があるような気がします。
     本手法は高速化のために、pre-pass時のNormalはRG8bitに格納し、メモリ帯域を節約し、その用途もAggregateの形成にのみ使用しています。しかし、これをSample単位で、しかもある程度精度のあるNormal格納方式に切り替えれば、当然ながら速度は低下しますが、ジオメトリの、Pixelに於ける法線の分布を計算することが出来るかもしれません。
     ある程度曲率のあるジオメトリのごく一部の領域(1Pixel以下の面積)が、高い輝度を生み出すようなShadingでは、通常のMSAAではいくらSample数を上げても、このような領域がSample位置と合致すると、非常に値の大きいシェーディング結果となります。ただし、本手法では、このような値を生む可能性のあるSampleが属するAggregateに、向きの異なる法線や位置が含まれれば、これをToksvigやLEANのような方法もしくは考え方で補完することで、Aggregate内で値が不必要に大きいシェーディング結果となることを防ぐことが出来るかもしれません。もちろん、単一のSampleがAggregateを形成し、高い輝度を生み出すShadingとなるときは、これを防ぐことは出来ませんが、従来のMSAAよりは同一サンプル数で良好な結果を得ることが出来る可能性は十分にあると思われます。
     また、本手法は、Per-PassのSample数を向上させても、Shadingのコストは基本的にAggregate数に依存するので、ShadingするSample数をあまり多くしないでも、高いSampling数でジオメトリをレンダリングをすることが可能です。将来的には8x以上のSampling数でpre-passを行うことが出来れば、高品位なレンダリングを現実的な時間でレンダリング可能かもしれません。