ピクセルシェーダー⑤ Sobel フィルター
今回からはピクセルシェーダーの実装例を紹介します。Sobel フィルターは輪郭抽出によく使われる畳み込みフィルターで、近傍のピクセルをサンプリングして、各ピクセルにカーネルと呼ばれる重みを乗算して加算します。
現在のピクセルの座標は uv 引数([0,1]で正規化)で渡されます。近傍のピクセルの位置を得るには画像のサイズが必要ですが、PsPad v1.0 には画像サイズを渡す定数レジスターが定義されていないので(v2.0で実装します!)、640 x 480 として位置を割り出しました。
float dx = 1.0/640.0;
float dy = 1.0/480.0;
中心のピクセルは使わないので、1ピクセルの生成に8回のサンプリングが必要です。PS_2.0でも16サンプラーがサポートされているので問題ありません。輝度値だけが欲しいので、Luminance 関数を作成します。
float luminance(float4 color)
{
return color.r*0.3 + color.g*0.59 + color.b*0.11;
}
UpLeft |
Up |
UpRight |
Left |
Right |
|
DownLeft |
Down |
DownRight |
float UpLeft = luminance(tex2D( implicitInputSampler,
float2(uv.x - dx, uv.y - dy)));
float Up = luminance(tex2D( implicitInputSampler,
float2(uv.x, uv.y - dy)));
float UpRight = luminance(tex2D( implicitInputSampler,
float2(uv.x + dx, uv.y - dy)));
float Left = luminance(tex2D( implicitInputSampler,
float2(uv.x - dx, uv.y - dy)));
float Right = luminance(tex2D( implicitInputSampler,
float2(uv.x + dx, uv.y - dy)));
float DownLeft = luminance(tex2D( implicitInputSampler,
float2(uv.x - dx, uv.y + dy)));
float Down = luminance(tex2D( implicitInputSampler,
float2(uv.x, uv.y+dy)));
float DownRight = luminance(tex2D( implicitInputSampler,
float2(uv.x + dx, uv.y + dy)));
以下のカーネルを使ってX方向とY方向の値を算出します。最終的なピクセル値はその二乗平均になります。
-1 |
0 |
1 |
-2 |
0 |
2 |
-1 |
0 |
1 |
X方向のカーネル
-1 |
-2 |
-1 |
0 |
0 |
0 |
1 |
2 |
1 |
Y方向のカーネル
float dX = -UpLeft - 2.0*Left - DownLeft + UpRight + 2.0*Right + DownRight;
float dY = -UpLeft - 2.0*Up - UpRight + DownLeft + 2.0*Down + DownRight;
float4 sobel = sqrt(dX*dX + dY*dY);
sobel.w = 1.0;
この畳み込みフィルターカーネルによる手法は、ぼかし(blur)など様々な画像処理に適用できます。
この Soble フィルター シェーダーは後ほど CodePlex の PsPad ページにアップロードします。