2022年11月27日日曜日

軽量なぼかし処理

以前DoFにチャレンジした際、ぼかし処理にガウシアンブラーを使ってぼかして見たが、思っていたほどボケなかった。
それにもかかわらず結構なGPUパワーを使うので、もっといい方法を探していた。

そこで見つけたのがこのサイト

ガウシアンブラー単体だと、ブラーをかける対象のサイズが大きいと処理の負担も大きい。
DoFでは1/4の縮小画像を作っていきながらブラーをかけて、その画像を拡大して使う。
ほぼそれと同じ発想なんだけど、ブラーの処理が例えば8×8ブラーだと16回サンプリングする必要があるのに対し、8回で済ませる。
あと、縮小画像1/16の画像と、1/64の画像の2段階で処理を行っていた。

最初真似して2段階、その後3段階まで試してみたけど、どうも結果が汚い。どんどん縮小させていって、最後に拡大するとどうしても粗が目立つ。
逆に最初から落としたいレベルまで縮小させてしまってから、縮小画像を4倍に拡大するようにしてみたところ、納得行く感じになった。

処理の流れ


PSシェーダ


#define DefRS "RootFlags(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | DENY_HULL_SHADER_ROOT_ACCESS | DENY_DOMAIN_SHADER_ROOT_ACCESS | DENY_GEOMETRY_SHADER_ROOT_ACCESS),DescriptorTable( Sampler(s0),visibility=SHADER_VISIBILITY_PIXEL),DescriptorTable( SRV(t0, flags=DATA_STATIC),visibility=SHADER_VISIBILITY_PIXEL),DescriptorTable( CBV(b0, flags=DATA_STATIC),visibility=SHADER_VISIBILITY_PIXEL)"

SamplerState Sampler : register(s0) ;

Texture2D TexColor : register(t0) ;

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 PSInput
{
	float4 Pos	: SV_POSITION ;
	float2 UV	: TEXCOORD ;
} ;
struct PSOutput
{
	float4 Col : SV_TARGET ;
} ;
PSOutput PSMain( PSInput In )
{
	PSOutput Out ;

	float4 color = 0 ;
	for( int j = 0 ; j < BLUR_SAMPLE_COUNT ; j++ ) {
		color += TexColor.Sample( Sampler, In.UV + BLUR_KERNEL[j] * Param.Scale ) ;
} Out.Col = float4( color.rgb / float( BLUR_SAMPLE_COUNT ), 1.0f ) ; return Out ; }

Param.Scaleに「1.0f / 元のテクスチャサイズ * ぼかし強度」を指定する。
1/4096の場合は、元のテクスチャ幅、高さそれぞれ64で割ったサイズの画像を用意して、ぼかし強度に64を指定してレンダリングをした後、元のテクスチャ幅、高さそれぞれ32で割ったサイズの画像を用意して、ぼかし強度に32を指定してレンダリングする。


出力結果


無料の写真素材「ぱくたそ」からお借りした画像

この画像をそれぞれの強度でぼかした結果がこれ。

1/16

1/64

1/256

1/1024

1/4096

中間バッファ


1/1024と1/4096の中間バッファはこんな感じ

1/1024の中間バッファ

1/4096の中間バッファ

この時点ではかなりカクカクしているけど、この後それぞれ1/512、1/2048の画像にアップスケーリングして、間をなだらかにするので元のサイズに戻してもあまり違和感がない状態になっている。


パフォーマンス


ガウシアンブラーは4KでGPU使用率が40%ぐらいに対し、今回の方式だと20%で半分になっていた。
PIXで測ってみると、1024×1024のサイズ同士ではガウシアンブラーが646usに対し、1/16縮小が365us、1/4096縮小が178usだった。
ボケの強度を強くするほど中間バッファのサイズが小さくなるので処理が軽くなる。

ガウシアンブラーは期待していたボケ具合にはならず、更に重い処理だったが、今回の方式であればボケ具合を調整できる上に、処理時間も半分以下ので済むので満足。

0 件のコメント:

コメントを投稿