2022年12月1日木曜日

CS版軽量なぼかし処理

前々回で、リアルタイムにぼかす処理を作ってみたけど、実際には静止画にぼかしフィルタを掛けているだけ。
ぼかす対象は静止画だったので毎フレーム更新する必要はない。

前に作ったのはリアルタイム用として、今回は1回だけの静止画用をコンピュートシェーダ(CS)で作って見ようと思う。


リアルタイム用は2枚の中間レンダーターゲットを用意して処理していた。
静止画用は空のテクスチャ2枚を用意して、CSで書き込むことになる。
テクスチャに書き込むのにUnorderedAccessView(UAV)をテクスチャに紐づけて、Dispatchする際には、用意したテクスチャではなくUAVの方を指定する。

#define DefRS "RootFlags(DENY_VERTEX_SHADER_ROOT_ACCESS|DENY_HULL_SHADER_ROOT_ACCESS|DENY_DOMAIN_SHADER_ROOT_ACCESS|DENY_GEOMETRY_SHADER_ROOT_ACCESS|DENY_PIXEL_SHADER_ROOT_ACCESS),DescriptorTable( Sampler(s0),visibility=SHADER_VISIBILITY_ALL),DescriptorTable( SRV(t0, flags=DATA_STATIC_WHILE_SET_AT_EXECUTE),visibility=SHADER_VISIBILITY_ALL),DescriptorTable( UAV(u0, flags=DATA_VOLATILE),visibility=SHADER_VISIBILITY_ALL),RootConstants( num32BitConstants=2, b0,visibility=SHADER_VISIBILITY_ALL)"

SamplerState Sampler : register(s0) ;
Texture2D TexColor : register(t0) ;        // ぼかし対象画像
RWTexture2D<float4> UAV : register(u0) ;    // 結果画像
struct _Param { 
	float2	Scale ;	// 倍率
} ;
ConstantBuffer<_Param> Param : register(b0) ;

static const int BLUR_SAMPLE_COUNT = 8 ;
static const float2 BLUR_KERNEL[BLUR_SAMPLE_COUNT] = {
	float2(-1.0f, -1.0f),
	float2(-1.0f,  1.0f),
	float2( 1.0f, -1.0f),
	float2( 1.0f,  1.0f),
	float2(-0.5f,  0.0f),
	float2( 0.0f,  0.5f),
	float2( 0.5f,  0.0f),
	float2( 0.0f, -0.5f),
} ;
						
struct CSInput
{
	uint3 ID : SV_DispatchThreadID ;
} ;

[RootSignature(DefRS)] 
[numthreads( 8, 8, 1 )]
void CSMain( CSInput In )
{
	float2 uv = In.ID.xy + 0.5f ;
	float4 color = 0 ;
	for( int j = 0 ; j < BLUR_SAMPLE_COUNT ; j++ ) {
		color += TexColor.Sample( Sampler, ( uv + BLUR_KERNEL[j]) * Param.Scale ) ;
	}
	UAV[ In.ID.xy ] = float4( color.rgb / float( BLUR_SAMPLE_COUNT ), 1.0f ) ;

	return ;
}

シェーダの内容はほとんど変わらない。
ただ1つ重要なポイントがあって、リアルタイム用の時は意識してなかったんだけど渡すテクスチャにミップマップをつけておくこと。
1段階目で一気に縮小するので、その画像をたった8個のサンプリングでは品質が保てなく、実はかなりの部分をミップマップに助けてもらっていた。ミップマップは事前(読み込み時)に1回だけの処理なので、パフォーマンスには影響がない。
だけど、リアルタイムの方ではレンダリング結果にブラーを掛ける場合はミップマップの恩恵は得られない。品質の話はブラー強度を徐々に高めたりして前後を比較して見たときに気になるレベルなので、強度が一定であればミップマップなしでも許容範囲だと思う。


パフォーマンス 


リアルタイムの方は、ぼかし強度によって365~178usだった。
CS版はぼかし強度によって276~14usぐらい。(Dispatch部分のみ)

結構軽そうなので毎フレーム処理するようにしてみたら432~166us。
ぼかし強度が強い場合はリアルタイムでも使ったほうがいい結果になった。

ぼかし強度が弱い場合、そんなにサンプリングしなくても品質が落ちないので試しにBLUR_SAMPLE_COUNTを4にしたら309~163usだった。
ぼかし強度に合わせてサンプル数を調節してやれば、CS版のほうが高速っぽい。

ライブラリにまとめる際、リアルタイム版は結果が中間レンダーターゲット、描画に必要なもう1つの中間レンダーターゲットも常に保持しておく必要があるのに対し、CS版は結果がテクスチャで、描画に必要だったUAV2つと、もう1つのテクスチャは削除できる。

0 件のコメント:

コメントを投稿