DirectXの話 第113回

Deferred MSAA

今回は主に Deferred Rendering で使用可能な Anti-Aliasing 技術として Deferred MSAA をやってみました。

個人的には、あんまり使い物にならないんじゃない?と思ったり思わなかったりなんですが。

現行GPUはそのほとんどが MSAA をサポートしています。

しかし、DirectX9世代のGPUは MRT での MSAA をサポートしていません。

そのため、Xbox360やPS3では Deferred Rendering (特に Deferred Shading) と MSAA の相性がよろしくありません。

解決手段として挙げられるのが Deferred Lighting を使用することで、これなら MRT を用いないので MSAA が使用できます。

とはいえ、Deferred Lighting に使用するすべてのバッファを MSAA で描画するというのはそれなりにコストがかかります。

できるなら、最後にポストフィルタ的に AA をかけたいわけです。

その手段として、エッジベースの AA がいくつか考案されています。

最近有名なものとしては MLAA というものがありますが、実装が難しく、GPUでは無理とも言われています。

ただ、GPUで実装したという人もいるので、割と何とかなるのかもしれません。

今回紹介する Deferred MSAA もそんな AA 技術の1つです。

実装はきわめて簡単です。

まず、MSAA を ON にしてシーンを描画します。

しかしこのとき、カラーバッファは描画しません。深度のみです。

次に MSAA を OFF にして普通にシーンを描画します。

もちろん普通に Deferred Rendering してもいいですし、Forward Rendering でも問題ありません。

ただし、各ピクセルの深度だけは取得できるようにしておきます。なので、Deferred Rendering の方がいいでしょう。

最後に、MSAA で描画した深度バッファの各サンプルをチェックし、そのピクセルの深度との差が閾値を超えていたらぼかします。

ぼかすカラーは任意のサンプリング位置でカラーバッファをバイリニア補間するだけです。

という解説だとわかりにくいような気もするので、シェーダのソースを見てみましょう。

float4 RenderDeferredMSAAPS( OutputVS inPixel ) : SV_TARGET

{

// 基準ピクセルの深度とカラーを取得する

float3 normal;

float depth;

DecodeNormalDepthLambert8888( normal, depth, texNormalDepth.Sample( samPoint, inPixel.texCoord ) );

float4 baseColor = texFinal.Sample( samPoint, inPixel.texCoord );

int2 uv_for_ms = (int2)(inPixel.texCoord * screenSize);

const float2 kSampleOffset[] =

{

float2( 0.380 - 0.5, 0.141 - 0.5 ),

float2( 0.859 - 0.5, 0.380 - 0.5 ),

float2( 0.141 - 0.5, 0.620 - 0.5 ),

float2( 0.620 - 0.5, 0.859 - 0.5 )

};

float3 outColor = 0;

for( int i = 0; i < 4; ++i )

{

float msDepth = texMSDepth.Load( uv_for_ms, i );

float delta = abs( msDepth - depth );

if( delta >= threshold.x )

{

float2 uv = inPixel.texCoord + kSampleOffset[i] / screenSize;

outColor += texFinal.Sample( samLinear, uv ).rgb;

}

else

{

outColor += baseColor.rgb;

}

}

outColor /= 4.0;

return float4( outColor, baseColor.a );

}

このサンプルは 4xMSAA の場合です。

まず、NoMSAA の深度とカラーを取得します。texNormalDepth が法線・深度マップ、texFinal がカラーバッファです。

ループは 4xMSAA なので4回です。

各ループで MSAA の深度を取得します。texMSDepth が MSAA の深度マップです。

注意点として、MSテクスチャはピクセル座標とサンプリング番号で取得します。

ピクセル座標なので、バッファが256*256であれば各座標は 0~255 で指定します。0.0~1.0 で指定して痛い目を見ないようにしましょう。

# 痛い目を見たんですけどね。

さて、取得した各サンプルの深度と基準深度の差を求め、その差が閾値以上であればカラーバッファからカラーを持ってきます。

このときに使用するテクスチャ座標のオフセットはDirectX11準拠のものです。

DirectX9ではまた違うオフセットになると思いますので注意してください。

トップの画像は Deferred MSAA を ON/OFF した場合の拡大画像なんですが、それなりに効いていることはわかると思います。

しかし、全体で見るとやっぱり弱いです。

なので、今回のサンプルは深度によるエッジ抽出以外に、法線によるものとカラーによるものを追加しました。

サンプルの操作方法は上下キーでカーソルの上下、左右キーで値を変更します。

"Disp Border" を true にすると境界部分を赤く表示します。

詳しくはソースを見てください。

ただ、やっぱりこの程度のシーンでは MSAA が効いているかどうかわかりにくいですね。

もうちょっとまともなシーンを作るべきか、どこかからもってくるべきか…。

では、今回はこれで終了です。

次回はやっぱり未定。