TOC: エフェクトとシェーダー
Back: テクスチャ座標
Next: マルチテクスチャ
ピクセルシェーダでその様なフィルタを適用するさいに解くべき問題の鍵は、まず第一に、現在のピクセルのテクスチャ座標を使ってどのようにピクセルの近傍にアクセスするかでしょう。
テクスチャの元サイズ(WIDTH/HEIGHT)が与えられ、そのテクスチャ空間での2ピクセル間の距離が計算できます。この数学は簡単です: もしテクスチャ座標の0がピクセルの0でテクスチャ座標の1がピクセルのWIDTHと指定すれば、あるピクセル座標からその水平の近傍への距離は1/WIDTHを表すことができます。よって、ピクセル単位のピクセルサイズはテクスチャ空間単位のピクセルサイズにおいて言うまでもなく1x1で次のように考えられます:
float2 pixelSize = 1 / float2(WIDTH/HEIGHT)
pixelSize.xyを使えば、ピクセル座標のto/fromの値で加算/減算することによって近傍ピクセルにアクセスできます。パッチの中でのこの計算を行いピクセルシェーダーの範疇を超えてこの結果を扱うにはこの様にするのがベストです
float2 PixelSize; //where .x will be the width and .y will be the height of pixels
簡単なアプリケーションの例は全てのピクセルの左隣、右隣を用いたエッジ検出フィルターで、これは隣のピクセルの差異を計算し、その絶対値が与えられた閾値よりも大きくなるかどうかを判定します。ピクセルは白色、または黒色になって差し支えなければ。
float2 PixelSize; float Threshold = 0.2; float4 PS(vs2ps In): COLOR { //the texture coordinate offset with vertical coordinate set to 0 float2 off = float2(PixelSize.x, 0); //sample the left and the right neighbouring pixels float4 left = tex2D(Samp, In.TexCd - off); float4 right = tex2D(Samp, In.TexCd + off); if (abs(ConvertToGray(left).x - ConvertToGray(right).x) > Threshold) return 1; else return float4(0, 0, 0, 1); }
上のコードでは2つの指摘があります:
エッジ検出を向上させるために、両方向でそれを行わなければなりません。よって左隣と右隣のピクセルに加えて上隣と下隣のピクセルもサンプリングする必要があります。
//the texture coordinate offset with horizonal coordinate set to 0 off = float2(0, PixelSize.y); //sample the upper and the lower neighbouring pixels float4 upper = tex2D(Samp, In.TexCd - off); float4 lower = tex2D(Samp, In.TexCd + off);
そして、私たちはその返却する色を決定する時には左右両方のピクセルを一緒に考慮しなければなりません:
if (abs(ConvertToGray(left) - ConvertToGray(right)).x > Threshold || abs(ConvertToGray(upper) - ConvertToGray(lower)).x > Threshold) ...
||はhlslではbooleanのORの意味で、左と右のピクセル間の差異、または上と下のピクセル間の差異のどちらかが閾値を超えた場合、ピクセルを白色で塗るという意味です。
ブラーもまたこの方法で実現できます。より良いブラーは、多くの近傍ピクセルを考慮します。ピクセル毎のテクスチャサンプリング数は16に制限され、良いブラーを達成する最良の方法は2通りでそれを行うことです。第一は水平方向に行います。その画像の結果にもう一度垂直方向にブラーをかけます。今のところは水平のブラーに集中します。
このアイデアは、近傍ピクセルの範囲をサンプルし、現在のピクセルからの距離に対応した影響で重み付けした色を加算すると言うものです。現在のピクセルは最大で重み付けされ、最左-最右のピクセルはより小さい重み付けがされます。結果は全ての重み付けの合計で除算され0..1の範囲で正規化されます。
float2 PixelSize; float4 PSHorizontalBlur(vs2ps In): COLOR { float4 sum = 0; int weightSum = 0; //the weights of the neighbouring pixels int weights[15] = {1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2, 1}; //we are taking 15 samples for (int i = 0; i < 15; i++) { //7 to the left, self and 7 to the right float2 cord = float2(In.TexCd.x + PixelSize.x * (i-7), In.TexCd.y); //the samples are weighed according to their relation to the current pixel sum += tex2D(Samp, cord) * weights[i]; //while going through the loop we are summing up the weights weightSum += weights[i]; } sum /= weightSum; return float4(sum.rgb, 1); }
このピクセルシェーダの垂直方向版を実現するために、PS()関数を複製し、PSVerticalBlur()という名前にして、この様に垂直方向のブラーを計算するようにコードを変更します:
float4 PSVerticalBlur(vs2ps In): COLOR { ... //7 upwards, self and 7 downwards float2 cord = float2(In.TexCd.x, In.TexCd.y + PixelSize.y * (i-7)); ... }
エフェクトの中に2つのピクセルシェーダー関数ができたので、2つのtechniqueが必要です:
technique THorizontalBlur { pass P0 { PixelShader = compile ps_2_0 PSHorizontalBlur(); } } technique TVerticalBlur { pass P0 { PixelShader = compile ps_2_0 PSVerticalBlur(); } }
2つのtechniqueの好きなほうを選んで使うことができます。しかしながらまず言及しておくと、良い品質のブラーを得るには、最初の(水平方向の)ブラーに2つ目の(垂直方向の)ブラーを適用することです。さらに詳しくは、次の後のマルチパスエフェクトのチャプターで説明します。
これらはピクセル近傍を元にした画像処理の基本です。しかし実のところ、似たようなエフェクトは一般的に畳み込み/カーネルフィルターに任せられています。学んだ事で、今までのところはあなたは自分の目標を達成できるべきです。詳細と例はこことここを見てください。
anonymous user login
~5h ago
~7d ago
~7d ago
~7d ago
~21d ago
~1mth ago
~1mth ago
~1mth ago
~1mth ago
~1mth ago