ぼかしフィルタ
それでは、ぼかしフィルタ(ガウシアンぼかし)を実装してみましょう。
原理
ぼかしフィルタでは、図4のように、入力ピクセルの色を周囲に拡散させるような処理を行います。
これをシェーダ上で実装するには、ピクセルシェーダでピクセルの出力色を計算する際に、そのピクセルの周囲一定距離内のピクセルの色をサンプリングして、それぞれ適当な係数を掛けた合計値を出力色にします(重み付き加算)。
ここでは、この重み係数に図6のようなガウス関数を用います。
また、ぼかし処理は、図7のようにまずX方向にぼかしてから、Y方向にぼかすという2段階に分けて行います。
このように本来2次元的なぼかし処理を1次元な処理2回に分割すると、1つのピクセルの色を計算するのに必要なサンプリング回数を大幅に減らすことが出来、高速化することができます。
例えばぼかしの範囲を縦横10ピクセルとすると、2次元で処理するならば10x10ピクセルの計100ピクセルをサンプリングする作業が必要になりますが、1次元な処理2回に分割すると、10ピクセルのサンプリングを2回行うだけで済むようになります。
なお、重み係数にガウス関数を用いた場合、その数学的特性により、このように処理をX方向とY方向に分割しても、分割しなかった場合と同様に円形にぼかすことができるという利点があります。
エフェクトファイルの編集
それでは、BasicPostEffect.fxをベースに、エフェクトファイルを編集していきます。
テクニックの2パス化
ぼかし処理を、いったんX方向にぼかした後、それを入力としてY方向にぼかす、というように2回に分けて行うため、X方向にぼかした結果を記録するためにレンダリングターゲットのテクスチャがもう一つ必要になります。
そのため、以下のようにレンダリングターゲットのテクスチャを2つに増やします。
texture ScnMap : RENDERCOLORTARGET < float2 ViewPortRatio = {1.0,1.0}; >; sampler ScnSamp = sampler_state { texture = <ScnMap>; MinFilter = LINEAR; MagFilter = LINEAR; MipFilter = NONE; }; // 以下を追加 texture ScnMap2 : RENDERCOLORTARGET < float2 ViewPortRatio = {1.0,1.0}; >; sampler ScnSamp2 = sampler_state { texture = <ScnMap2>; MinFilter = LINEAR; MagFilter = LINEAR; MipFilter = NONE; };
これに合わせて、ピクセルシェーダもX方向とY方向の処理のものに増やし、名前も変更します。頂点シェーダについては共通で使用するので増やす必要はありません。
VS_OUTPUT VS_DrawBuffer( float4 Pos : POSITION, float4 Tex : TEXCOORD0 ){ VS_OUTPUT Out = (VS_OUTPUT)0; Out.Pos = Pos; Out.Tex = Tex + ViewportOffset; return Out; } // 関数名を変更 float4 PS_GaussianX(float2 Tex: TEXCOORD0) : COLOR { float4 Color = tex2D( ScnSamp, Tex ); // 何か処理 return Color; } // 以下を追加 float4 PS_GaussianY(float2 Tex: TEXCOORD0) : COLOR { // 入力するテクスチャをScnMapからScnMap2に変更 float4 Color = tex2D( ScnSamp2, Tex ); // 何か処理 return Color; }
また、テクニックのパスやスクリプトの記述も追加します。
technique PostEffect < string Script = "RenderColorTarget0=ScnMap;" "RenderDepthStencilTarget=DepthBuffer;" "ClearSetColor=ClearColor;" "ClearSetDepth=ClearDepth;" "Clear=Color;" "Clear=Depth;" "ScriptExternal=Color;" // 以下を追加 "RenderColorTarget0=ScnMap2;" "RenderDepthStencilTarget=DepthBuffer;" "ClearSetColor=ClearColor;" "ClearSetDepth=ClearDepth;" "Clear=Color;" "Clear=Depth;" "Pass=GaussianX;" "RenderColorTarget0=;" "RenderDepthStencilTarget=;" "Pass=GaussianY;" //パス名を変更 ; > { // パス名とピクセルシェーダの関数名を変更 pass GaussianX < string Script= "Draw=Buffer;"; > { AlphaBlendEnable = FALSE; VertexShader = compile vs_2_0 VS_DrawBuffer(); PixelShader = compile ps_2_0 PS_GaussianX(); } // 以下のパスを追加 pass GaussianY < string Script= "Draw=Buffer;"; > { AlphaBlendEnable = FALSE; VertexShader = compile vs_2_0 VS_DrawBuffer(); PixelShader = compile ps_2_0 PS_GaussianY(); } }
このように記述することで、まずテクスチャScnMapに各オブジェクトが描画され、その後のパスGaussianXの処理結果がテクスチャScnMap2に描画されます。
この時点ではまだ、実行結果はBasicPostEffect.fxと変化ありません。