トップ回答者
標準のShaderEffect(Blur)のコード

質問
-
WPFというより、HLSLの質問なのですが、よろしくお願いします。
.net3.5 SP1 に標準で搭載されている ShaderEffect に、BlurEffect というタイプがありますが、この処理と同じようなWPFのShaderEffectとして使えるHLSLのコードやサンプルはありますでしょうか。
BlurEffectに独自のパラメータを追加したEffectを作りたいと思うのですが、HLSLについてはまったく?な状態で目が回ってしまいました。
標準のBlurEffectには、以下のプロパティがあります。
A)KernelType: Gaussian / Box
B)Radius:0~100
C)RenderingBias:Quality / Performance
C)たぶんアルゴリズム上のトレードオフだと思いますがこれはおいといて、GaussianでRadiusが指定できればOKです。
自分でやってみましたが、どうもテクセルの概念を正しく理解していないようで、標準のBlurのように綺麗なぼかしにならずに、しましまになってしまいます。
あと、サンプルの上限が16のようですが、3*3や5*5ならわかるんですけど、4*4というのは、いったいどこをサンプリングするのか?で、はまってしまいました。
CodePlexの ShaderEffectLibrary - ZoomBlur などを眺めていましたが、わかりませんでした。
ご存じの方がいらっしゃいましたら、よろしくお願いします。
回答
-
いや、そもそも標準BlurEffectのWPF用コード自体、どこかにあるんじゃないか? ← 今ココ
ShaderEffect のコードが公開されているかどうかは分かりません.
もし公開されている (or 将来公開されることがある) としたら- 参照用のライセンスで公開されている .NET Framework のソースコード に含まれる
- WPF 関係者の blog 記事でたまたま公開されている
- CodePlex や MSDN Code Gallery でこっそり公開されている
あたりだと思います.
標準Blurのような処理は、シングルパスでもできるんでしょうか。
標準Blurのアルゴリズムがどうなっているのかよく分からないので何とも言えないところです.とりあえず,英語版 MSDN Forums の WPF のフォーラムで聞いてみると,開発者の人が答えてくれる可能性はあります.
とりあえず気になる点ですが,ソース画像 (動画?) に直接ブラーをかけたいのでしょうか? それとも,画面上の領域サイズに縮小してからブラーをかけたいのでしょうか.
例えば 1024 pixel × 1024 pixel の動画があるとして,画面上では 256×256 に縮小表示されているとします.このとき,3 × 3 のブラーをかけたいとは,元画像・画面サイズどちらでの 3 × 3 なのでしょうか?
あと,単に HLSL の実験をするだけなら,FX Composer とか Render Monkey といったツールで実験した方が楽かもしれません.WPFのエフェクトをマルチパスでかける方法ってあるのでしょうか。
確か出来たはずです.WPF "multi-pass" 等で検索してみて下さい.
すべての返信
-
Shader Effects BuildTask and Templates という記事があります。
http://blogs.msdn.com/greg_schechter/archive/2008/08/11/a-visualstudio-buildtask-and-project-and-item-templates-for-writing-shadereffects.aspx
ここにはソースもあります。
http://wpf.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=14962
Blur だとこのページが参考になるでしょう。
http://windowsclient.net/wpf/wpf35/wpf-35sp1-more-effects.aspx
えムナウ@わんくま同盟 Microsoft MVP Visual Studio C# Since 2005/01-2009/12 -
>えムナウさん
情報ありがとうございます。
私の説明が不足しておりました。すみません。
質問の経緯を詳しく↓に書きます
1.CodePlexから、下記のコードを参考にして、カスタムエフェクトを作成できるようにしました
【ShaderEffectをビルドするためのインストーラー】 ← えムナウさんからも紹介いただいたページ
WPF Futuresの一部
http://wpf.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=14962
【各種エフェクトライブラリとそれを使ったサンプルアプリ】
http://www.codeplex.com/wpffx
http://shaderpad.codeplex.com/
2.標準のBlurエフェクトを拡張したエフェクトを作るために、まずは標準と同じ動作をするようなBlurエフェクトの実装を試しました
上記のMotionBlurや、RadialBlurのサンプルを参考にしつつ試行錯誤
HLSLのコードは↓のような感じです。
sampler2D implicitSampler_s0 : register(s0); float float_c0 : register(c0); // Blurの半径 float4 main(float2 uv : TEXCOORD) : COLOR { float4 output = 0; float2 offset = float_c0/100.0; int count = 4; float2 uvTmp = uv; float4 clrTmp = 0; output = tex2D(implicitSampler_s0, uv ); // 周囲4x4をサンプリングして合計を計算 for(int x=count/2 * -1; x < count/2; x++) { for(int y=count/2 * -1; y < count/2; y++) { uvTmp.x = uv.x - offset.x * x; uvTmp.y = uv.y - offset.y * y; clrTmp += tex2D(implicitSampler_s0, uvTmp ); } } // 平均値を結果として返す clrTmp/=(count*count); return clrTmp; }
3.品質的に標準Blurのように綺麗にならない
ExpressionBlend3を使って標準ブラーをかけた画像と、自作のBlurエフェクトをかけた画像の比較をアップしました。
http://cid-baa56d6884983c0d.skydrive.live.com/self.aspx/NIM%7C_WPF/%e5%a4%b1%e6%95%97Blur%ef%bc%88%e5%bc%b1%ef%bc%89.jpg
自分で作ったエフェクトは、特に強度を上げるとBlurとは呼べないような縞々画像になってしまいます
1パスで、16個しかサンプルしていないので、半径が大きくなるほど荒さが出てしまいます。
CodePlexのMotionBlurなども1パスなので、半径が大きくなると縞々になります。
4.どうやったら標準のBlurと同じくできるか悩む
WPF標準のBlurは、マルチパスでエフェクトをかけてるかも・・・
↓
Blurみたいな超ベーシックなエフェクトならどこかにアップされてないか?
↓
いや、そもそも標準BlurEffectのWPF用コード自体、どこかにあるんじゃないか? ← 今ココ
という状況です。
手始めにBlurでも、とか甘く見ていたら全然だめでした。
標準Blurのような処理は、シングルパスでもできるんでしょうか。
WPFのエフェクトをマルチパスでかける方法ってあるのでしょうか。
ご存じの方がいらっしゃまいましたら、よろしくお願いします。
(WPF以外の実装方法参考)
http://d.hatena.ne.jp/kataho/20061025/p1
http://www.t-pot.com/program/79_Gauss/index.html- 編集済み NIM5 2009年6月24日 13:38
-
いや、そもそも標準BlurEffectのWPF用コード自体、どこかにあるんじゃないか? ← 今ココ
ShaderEffect のコードが公開されているかどうかは分かりません.
もし公開されている (or 将来公開されることがある) としたら- 参照用のライセンスで公開されている .NET Framework のソースコード に含まれる
- WPF 関係者の blog 記事でたまたま公開されている
- CodePlex や MSDN Code Gallery でこっそり公開されている
あたりだと思います.
標準Blurのような処理は、シングルパスでもできるんでしょうか。
標準Blurのアルゴリズムがどうなっているのかよく分からないので何とも言えないところです.とりあえず,英語版 MSDN Forums の WPF のフォーラムで聞いてみると,開発者の人が答えてくれる可能性はあります.
とりあえず気になる点ですが,ソース画像 (動画?) に直接ブラーをかけたいのでしょうか? それとも,画面上の領域サイズに縮小してからブラーをかけたいのでしょうか.
例えば 1024 pixel × 1024 pixel の動画があるとして,画面上では 256×256 に縮小表示されているとします.このとき,3 × 3 のブラーをかけたいとは,元画像・画面サイズどちらでの 3 × 3 なのでしょうか?
あと,単に HLSL の実験をするだけなら,FX Composer とか Render Monkey といったツールで実験した方が楽かもしれません.WPFのエフェクトをマルチパスでかける方法ってあるのでしょうか。
確か出来たはずです.WPF "multi-pass" 等で検索してみて下さい.
-
NyaRuRUさん。多くの情報をありがとうございます。
まだ全部試せていませんが、ひとまずお返事させていただきます。
>参照用のライセンスで公開されている .NET Framework のソースコード に含まれる
3.5SP1 で追加されたコードがすべて含まれているそうですが、"Blur"とかで検索してみましたが、簡単には見つかりませんでした。
http://www.atmarkit.co.jp/fdotnet/insiderseye/20080222sourcecode/sourcecode.html
ステップインもできるそうなのでやってみたのですが、Vista SP2ではステップインできないとの情報もあり、環境を組み直してからチャレンジしてみます。
http://social.msdn.microsoft.com/Forums/ja-JP/vsgeneralja/thread/2170cfb6-4470-4f7e-bfe4-f1de60bad666
これでヒントが得られるんじゃないかと、かなり期待してます。
>WPF 関係者の blog 記事でたまたま公開されている
>CodePlex や MSDN Code Gallery でこっそり公開されている
GregさんのblogやCodePlexは定期的にチェックしてましたが、MSDN Code Galleryは見てませんでした。
いくつか検索した範囲では見つかりませんでしたが、定期的にチェックしてみます。ありがとうございます。
>とりあえず気になる点ですが,ソース画像 (動画?) に直接ブラーをかけたいのでしょうか? それとも,画面上の領域サイズに縮小してからブラーをかけたいのでしょうか.
>例えば 1024 pixel × 1024 pixel の動画があるとして,画面上では 256×256 に縮小表示されているとします.このとき,3 × 3 のブラーをかけたいとは,元画像・画面サイズどちらでの 3 × 3 なのでしょうか?
とりあえずは、「画面上の領域サイズに縮小してからブラーをかけたい」です。
WPF標準のBlurEffect.Radiusプロパティのヘルプを見ると、「ぼかし効果の曲線の半径を示す値を取得または設定します」とあり、デフォルトでは5が設定されます。
この単位がテクセルなのかピクセルなのかは書いてありませんでしたが、「5」とかいう数字を見る限り、おそらくピクセルだろうと推測します。
ExpressionBled3で、画像にBlurEffectを設定し、Radiusを5にして、ウィンドウの表示倍率を大きくしていくと、だんだんぼかしの強度が弱くなっていくような感じがしました。
私がチャレンジしているのは、HLSLエフェクトを使ったフォトレタッチソフトの実験なので、同じ半径でも、画面の拡大率によってぼかしの強度が変わってしまっては困るのですが、それはアプリコード側で出力解像度に合わせてRadiusの単位をテクセルからピクセルに計算し直すことで実現できると思います。
現在、CodePlexのBlur系エフェクトのサンプルを見ると、サンプリング数は16固定(PS2.0の仕様)なので、半径がいくつであっても、4x4個のサンプリングで実現していました。
半径によって中心からサンプルするテクセルの距離が変わるような実装になってます。
たとえば、半径0.05でサンプリングする場合(x方向のみ)
u-0.05, u-0.025, u, u+0.025, u+0.05
のテクセルの値を合計を5で割りますが、半径0.2でサンプリングする場合
u-0.2, u-0.1, u, u+0.1, u+0.2
の合計を5で割ります。私が作ってみたBoxBlurのテストコードもこんな感じです。
実際には5x5はサンプリングの上限をオーバーしてしまうので、4x4でやってます。
u-0.2, u-0.1, u ,u+1.0
を4で割ってます。(これを書いてて気がつきましたが、 u-0.2, u-0.1, u+0.1, u+0.2 を 4 で割るべきでしたね・・・)
サンプル数が固定でBlur半径を大きくすると、サンプリングの間隔がどんどん大きくなっていって、実ピクセルよりもサンプリング間隔が大きくなると、縞模様になってしまうのでは?と推測しています。
WPF標準のBlurEffectクラスでは、半径をいくら大きくしても縞模様にならないので、どうやって実現しているんだろう・・・という感じです。
>確か出来たはずです.WPF "multi-pass" 等で検索してみて下さい.
下記のページを参照させていただきました。
#In this release of WPF, we do not have explicit support for building multi-pass effects.
#このリリースのWPFでは、マルチパスエフェクトの明確なサポートはしません
・・・残念。
代替案として書かれていた方法は、BorderやCanvasなどのパネルコントロールを入れ子にして、各パネルごとに異なるShaderEffectを設定すれば、1つのUIElementに複数のEffectを適用できるよ、ということのようです。
たとえば、グレイスケールをかけつつぼかしをかける場合は、↓のようにせよ、ということみたいです。
<Canvas x:Name="Layer1" Effect = fx:GrayScaleEffect>
<Canvas x:Name="Layer2" Effect = fx:BlurEffect >
<Image Source="1.png" />
</Canvas>
</Canvas>
CodePlexのPixelShaderEffects Library のサンプルアプリでも、Borderを入れ子にして実現していたので、おそらくこれしかないんだろうと思います。
http://www.codeplex.com/wpffx
したがって、下記のようにすれば、16x16サンプリングによるぼかしが実現できるということですね。
<Canvas x:Name="Layer1" Effect = X方向のブラー>
<Canvas x:Name="Layer2" Effect = Y方向のブラー >
<Image Source="1.png" />
</Canvas>
</Canvas>
ひとまずこれもチャレンジしてみます。
1パス目は単なる調査のみで、2パス目で調査結果を使ってエフェクトをかける、という場合はどうするのかな?
一旦、バッファとなるVisualBrushに1パス目の調査結果を出力して、それを2パス目担当のCanvasに配置してEffectをかける・・・みたいな感じでしょうか。
>あと,単に HLSL の実験をするだけなら,FX Composer とか Render Monkey といったツールで実験した方が楽かもしれません.
ありがとうございます。
画面に圧倒されて腰が引けて手が出せていませんでした。WPFの2D Effectに対するPixelShaderに特化したツールがあれば・・・。
余談ですが、私はNyaRuRuさんが公開された↓のアプリに、複数のレジスターパラメータを設定できるように拡張したツールを使ってテストしています。(同じ方ですよね?)
ものすごく重宝しています。重ねてお礼申し上げます。
http://d.hatena.ne.jp/NyaRuRu/20080813/p1- 編集済み NIM5 2009年6月30日 9:20
-
ひとまず、X方向のBlurとY方向のBlurを分離して、borderを2層構造にして、それぞれのborderのEffectに、Y方向とX方向のBlurを設定してみました。
これで、16x16のサンプリングになり、ぼかしの範囲を大きくしても綺麗にぼけるようになりました。
もちろん、半径を極端に大きくすれば縞が見えますが、だいぶ実用的になりました。
現在は単純なBoxBlurですが、GaussianBlurになるように拡張してみます。
WPF標準のBlurがどうやって実現しているのかは未だ不明ですが、いずれ情報が出てくることを期待します。
開発者のブログと、英語版MSDNのフォーラムが参考になりました。
ありがとうございました。