circle-loader
by
55/ 0

5. Kawase Blur

5.1 基礎知識

GDC2003で川瀬さんが発表した「Frame Buffer Postprocessing Effects in DOUBLE-S.T.E.A.L (Wreckless)」では、Bloom効果に適用したアルゴリズムが紹介されました。その後ぼかしアルゴリズム「Kawase Blur」として普及されてきました。 .

このプレゼンテーションは、GDCのウェブサイトにてご覧いただけます。

https://www.gdcvault.com/play/1022665/Frame-Buffer-Postprocessing-Effects-in

https://www.gdcvault.com/play/1022664/Frame-Buffer-Postprocessing-Effects-in

添付ファイルにはこのスピーチのPPTがあります。

添付ファイル: GDC2003_DSTEAL.ppt https://uploader.shimo.im/f/E0dNiYPaUKGqORTp.ppt?accessToken= eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ. eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTM1NzQ0OTUsImZpbGVHUlEIjoiMU03TXpuZVRSSlFRV3RJZSIsImlhdCI6MTY1MzU3NDE5NSwidXNlcklkIjo3MjI0ODE3OX0 .KXPZRBuJd9DwLMVaBeQjMnhda8xOHS-ZQvo96N0bGbA

前節で述べたBox Blurアルゴリズムの最適化のアイデアは、時間的複雑さを定数のレベルにまで下げることです。一方、Kawase Blurアルゴリズムは、ハードウェアに焦点を当て、GPUによるバイリニア補間のサンプリングのオーバーヘッドが小さいことを最大限に利用して、アルゴリズムの最適化を実行します。

GPU によるバイリニア補間のサンプリングについては 3 章で説明しているので,ここでは詳細な説明をしません。例えば、3×3の画素領域の場合。

(GDC2003で川瀬さんが共有したPPTより)

図に示すように、黒い点で表された画素を処理するとき、赤い点で示された周囲の4つのサンプリング点を選択し、データの平均値を求めます。サンプリングに選ばれた点(=赤い点)は、4つの画素の中点である。そして、GPUは周囲の4つのピクセルをサンプリングしてバイリニア補間を行い、その結果をサンプリングデータとして生成します。このように処理した結果は、図中の右側に示した重みを持つ3×3の畳み込みカーネルで処理する結果と同じです。でも、このような方法は、1画素のテクスチャ読み込み回数を9回から4回に削減しました。

ぼかし効果を最大化するために、従来のアルゴリズムの経験を考慮し、複数回のぼかしを行うことや、より多くの画素をサンプリングすることを選択することが可能です。例えば、最初のぼかしとして上記の方法を使用した場合、次のぼかしは、以下の画像のようにサンプリング点のオフセットを大きくして行うことができます。

(GDC2003で川瀬さんが共有したPPTより)

 

ぼかし処理手段として、毎回、処理対象画素からサンプリング点までのオフセットを自由に選択することができます。このようにして、より多くの画素がぼかし演算に参加できるようになりました。何度かぼかすと、ガウスぼかしに近似した効果が得られます。


5.2 Unityの実装

上記のアルゴリズムに基づき、Unityで川瀬ブラーを実装します。0、1、2、2、3のカーネルの5パスKawaseフィルターを選択して、35×35の畳み込みカーネルを用いたGaussian Blur「まとめ」の部分でさらに説明)の効果と比較します。

最初にKawase Blurサンプリングのアルゴリズムを実装します。簡単なので、オフセットに対応する位置でテクスチャのデータを読み込んで平均化演算を行うだけで済みます。

float4 KawaseBlur(pixel_info pinfo, int pixelOffset)
{
	float4 o = 0;
	o += tex2D(pinfo.tex, pinfo.uv + (float2(pixelOffset + 0.5, pixelOffset + 0.5) * pinfo.texelSize)) * 0.25;
	o += tex2D(pinfo.tex, pinfo.uv + (float2(-pixelOffset - 0.5, pixelOffset + 0.5) * pinfo.texelSize))* 0.25;
	o += tex2D(pinfo.tex, pinfo.uv + (float2(-pixelOffset - 0.5, -pixelOffset - 0.5) * pinfo.texelSize)) * 0.25;
	o += tex2D(pinfo.tex, pinfo.uv + (float2(pixelOffset + 0.5, -pixelOffset - 0.5) * pinfo.texelSize)) * 0.25;
	return o;
}

オフセットが異なるフラグメントシェーダーを実装する。

float4 frag0(v2f_img i) : COLOR
{
	pixel_info pinfo;
	pinfo.tex = _MainTex;
	pinfo.uv = i.uv;
	pinfo.texelSize = _MainTex_TexelSize;
	return KawaseBlur(pinfo, 0);
}

float4 frag1(v2f_img i) : COLOR
{
	pixel_info pinfo;
	pinfo.tex = _GrabTexture;
	pinfo.uv = i.uv;
	pinfo.texelSize = _GrabTexture_TexelSize;
	return KawaseBlur(pinfo, 1);
}

float4 frag2(v2f_img i) : COLOR
{
	pixel_info pinfo;
	pinfo.tex = _GrabTexture;
	pinfo.uv = i.uv;
	pinfo.texelSize = _GrabTexture_TexelSize;
	return KawaseBlur(pinfo, 2);
}

float4 frag3(v2f_img i) : COLOR
{
	pixel_info pinfo;
	pinfo.tex = _GrabTexture;
	pinfo.uv = i.uv;
	pinfo.texelSize = _GrabTexture_TexelSize;
	return KawaseBlur(pinfo, 3);
}

同様に、GrabPass{}関数を利用して、前のぼかし効果を得ることができます(この関数はまだ比較的に時間がかかるので、OnRenderImage()関数のblitの使用を検討してください)、5 passを実装します。

Pass
{
	CGPROGRAM
	#pragma target 3.0
	#pragma vertex vert_img
	#pragma fragment frag0
	ENDCG
}

GrabPass{}

Pass
{
	CGPROGRAM
	#pragma target 3.0
	#pragma vertex vert_img
	#pragma fragment frag1
	ENDCG
}

GrabPass{}

Pass
{
	CGPROGRAM
	#pragma target 3.0
	#pragma vertex vert_img
	#pragma fragment frag2
	ENDCG
}

GrabPass{}

Pass
{
	CGPROGRAM
	#pragma target 3.0
	#pragma vertex vert_img
	#pragma fragment frag2
	ENDCG
}

GrabPass{}

Pass
{
	CGPROGRAM
	#pragma target 3.0
	#pragma vertex vert_img
	#pragma fragment frag3
	ENDCG
}

得られた結果

(上記方法で得られた結果)

 

(35×35 Gaussian Blurの結果)

この処理で得られる結果は、35×35の畳み込みカーネルを用いたGaussian Blurで得られる結果と似ていることがわかります。


5.3 まとめ

Kawase Blurアルゴリズムの利点は、GPUのハードウェア機能を最大限に活用することで、アルゴリズムの性能が非常に良くなることです。しかし、Kawase Blurアルゴリズムのぼかし効果の質の推定は、試行錯誤が必要な経験値です。これまでのアルゴリズムでは、ぼかし効果の質を測定するための畳み込みカーネル数とぼかし回数が決まっており、畳み込みカーネルサイズが大きく、ぼかし回数が多いほど良い結果が得られるようになっています。Kawase Blurで設定したオフセットは、演算子と同様にぼかし効果に質的な影響を与えません。そこで、ガウスぼかし操作の効果に近くために、いくつかの異なるオフセット値を選択し、いくつかのぼかし操作を行う必要があります。以下の実験は、Intelの研究成果に由来します。

リンク: https://www.intel.com/content/www/us/en/developer/articles/technical/an-investigation-of-fast-real-time-gpu-based-image-blur-algorithms.html

入力値:黒い背景に12×12の白い画素のブロック(左:X軸の中央断面、右:上面図)。

35×35のガウシアンフィルターを適用した値。

0, 1, 2, 2, 3 カーネルの 5 パス川瀬フィルターを適用した値 – 35×35 ガウシアンフィルターに非常に近い(ただし完全には一致しない):.

Kawase Blurアルゴリズムの欠点は、複数のぼかし処理を行うと、それに応じた数のDrawCallが発生することです。

例えば、上記の例では、5回のKawase Blur計算で5回のDrawCallが発生しますが、1画素を処理するのに4*5=20回だけのテクスチャ読み込みが必要となります。同様の 35×35 畳み込みカーネルを持つ Gaussian Blur linear sampling アルゴリズムは、2 つの DrawCall を生成し、1 ピクセルを処理するために 17*2=34 のテクスチャ読み込みを必要とします。 処理する画像を1枚に拡張した場合、DrawCallの回数が増えるとデメリットは、テクスチャの読み込み回数の大幅な減少による速度向上で相殺されるほどです。つまり、Kawase Blurは、効果も性能も優れているのです。

DEMO:https://github.com/UWA-MakeItSimple/Course-PostProcessingEffect/blob/main/Assets/Shaders/Blur/KawaseBlur.shader


UWA公式サイト:https://jp.uwa4d.com

UWA公式ブログ:https://blog.jp.uwa4d.com

UWA公式Q&Aコミュニティ(中国語注意):https://answer.uwa4d.com