DirectXの話 第116回

Subpixel Reconstruction Anti-Aliasing

今回はGDC11で発表されていた Subpixel Reconstruction Anti-Aliasing(SRAA) をやってみました。

以前やった Deferred MSAA に極めて似ている、というか基本アプローチは全く同じものです。

Deferred Rendering とも相性が良く、また、MLAA との併用も可能(重くなるのでおすすめできませんが)です。

Super Sampling Anti-Aliasing(SSAA) や Multi Sampling Anti-Aliasing(MSAA) は各ピクセルに対してn個のサブピクセルを定義します。

SSAAではそのサブピクセルごとにピクセルシェーダを起動し、その結果と深度を格納します。

MSAAではピクセルごとにシェーダを起動し、ポリゴンが覆っているサブピクセルにはそのシェーダの結果と各サブピクセルの深度を格納します。

最終的にサブピクセルに格納されたカラーを平均してピクセルカラーとするのが SSAA、および MSAA のアルゴリズムです。

Deferred MSAA の記事でも書いたとおり、Deferred Rendering とこれらの技術は相性が良くありません。

Deferred MSAA では、最初に描画する形状を保持した GBuffer のみを MSAA で描画し、あとは普通に描画、最後にポストプロセス的にエッジをぼかすことで対応しようというものでした。

SRAA も同じです。若干のアプローチの違いはありますが、ほぼ同じと言ってもいいでしょう。

SRAA ではエッジ部分をボックスフィルタやそれに準ずるフィルタでぼかします。

その際のウェイト値は距離ではなく、エッジの強さ(深度の差や法線の向き)によって算出します。

発表された論文ではバイラテラルフィルタを使用しています。

では、実装をもう少し詳しく見ていきましょう。

まず最初に GBuffer に法線と深度を格納します。この際に MSAAありとなしで2回の描画を行っています。

SRAA では MSAAありの1回だけで済むのですが、Deferred MSAA も実装しているためと、Z Pre-Pass のためにMSAAなしのものも使用しています。

次に MSAAなしの GBuffer を利用して LightBuffer を描画し、Light Pre-Pass で最終バッファも描画します。

最後にポストプロセスで AA を行います。この辺も Deferred MSAA と同じですね。

基本は3*3のボックスでウェイト値を求めます。これをn回行って、その全てのウェイト値を加算します。

最終的に各ウェイト値を積算した3*3のピクセルカラーをブレンドします。

n はサブピクセルの数です。4xMSAA なら n = 4 となります。

バイラテラルは論文中のCUDAコードと同じものを利用させて貰っています。

float CalcBilateral( float3 centerN, float centerD, float3 tapN, float tapD )

{

float n = 1.0 - dot( centerN, tapN );

float d = depthScale * abs( centerD - tapD );

return exp( -weightScale * max( n, d ) );

}

Deferred MSAA では深度のみを利用していましたが、こちらは法線の方向も見ています。

ただし、スケーラビリティとして、深度のみを使用する方法もあると論文に書かれています。

また、これよりも高い精度を保つのであれば、平面の方程式を用いる方が良いとも書いてあります。

平面の方程式を用いる場合は深度から座標を求めなければなりません。9*n 個の計算を行うのはちょっと厳しいのではないかと思います。

バイラテラルフィルタは通常エッジ部分を残しつつぼかすものなので、そのまま使うと逆にエッジ部分だけ残ります。

今回の実装では中心ピクセルに予め 1.0 のウェイト値を置いておき、1.0 からフィルタのウェイト値を引いたものを各ピクセルのウェイト値に加算するようにしました。

調整パラメータを上手く選んでやればかなりましな結果になるはずです。

サンプルは以下から落としてください。

カーソルキーでパラメータの変更、P キーで一時停止解除になります。

今後の課題としては、MSAAなしの GBuffer をなくす必要があると思います。

ライト計算等は MSAAありの GBuffer の0番目を利用することで対応できると思います。

ただ、Z Pre-Pass が使えなくなるので、MSAAありで描画した際の深度バッファをコピーするなどして利用できるようにする必要があるでしょう。

それらも決して軽くないとは思うのですが、モデルを複数回描画するよりマシでしょう。

出来たらここに追記する予定です。

追記:

MSAAなしの GBuffer を描画せずにSRAAをやってみました。

結論から行くと、微妙です。

この程度のシーンであればオブジェクトの複数回描画よりSRAAの処理自体の重さがネックになります。

うちの環境では、Deferred MSAAで270FPS、SRAAで150FPS、改造SRAA(今回のやつ)で170FPSとなりました。

また、Light Pre-Pass と今回の手法は相性が悪いです。

というのも、この方法では LightBuffer の処理まではサブピクセル0番で行いますが、最終描画では MSAAなしでモデルを描画するため、サブピクセルからずれる場所が出てきてしまいます。

つまり、サブピクセルでは奥にあるポリゴンが選択されているのに、最終描画では手前のポリゴンが有効になってしまう場面が出てくるわけです。

この状況ではエッジ部分のライトがおかしくなる場所が出てきてしまい、映像のクオリティが保てません。

しかし Deferred MSAA の要領でやっていくと3回もオブジェクトを描画しなければならないわけで…。

現状、この手の手法は使い道に困る感じです。Deferred Shading ならいいんですが、マテリアルの関係で出来れば Light Pre-Pass を使いたいしなぁ…。