モーションブラーエフェクトをWPFアプリケーションに追加する



こんにちは、Habr!

毎秒24フレームが人間の視覚が知覚できる最大値であるというフレーズを何度も耳にしました。 したがって、ビデオを1フレームだけ加速すると、視聴者の潜在意識に情報を埋め込むことができます。 そしてもちろん、私たち全員がこれが真実でないことを知っています 。 デジタルカメラのマトリックスのフォトダイオードと同様に、網膜ニューロンは特定のポイントで瞬間的な照明を記録しませんが、短時間の全光束を記録します。 さらに、私たちの脳はこのような視覚の特徴に慣れているため、物体の個々の写真で構成されるビデオは不自然に思えます。 コンピューターアニメーションについても同じことが言えます。 アニメーションアーティストは、キャラクターの背後にぼやけた列車を描くことを長い間学びました。この手法は「モーションブラー」と呼ばれ、2Dおよび3Dアニメーションのすべての最新パッケージで利用できます。 しかし、普通のデスクトッププログラマはどうでしょうか。 この記事では、ポップアップが表示されたときにレスポンシブ効果を与えるために、モーションブラーをWPFアプリケーションにねじ込んだ方法について説明します。



手始めに、私は2つの写真を見ることを提案します。 それらのフレームの数は同じです。

通常のアニメーション モーションブラー
モーションブラーのないウィンドウ




モーションブラーのあるウィンドウ






違いがわからない場合は、私が何晩か無駄にしたと思います しかし、私は違いがまだ顕著であると信じたい:)



そして、フレームごとのスキャンでは次のようになります。

通常のアニメーション
モーションブラー


注意深い読者は、最下行の各フレームが互いに重ね合わされた15枚の半透明の画像で構成されているため、FPSを15倍にしたと言えるでしょう。 冗談。



ボンネットの下を見てください?



ピクセルシェーダー


最も強力な最新の中央処理装置でさえ、ブレーキなしのモーションブラーエフェクトを実現することはほとんど不可能であるため、この例では、GPUがぼやけた「トレース」のレンダリングに重要な役割を果たします。 WPFのピクセルシェーダーのサポートにより、影、歪み効果(虫眼鏡、ねじれ、波紋)、カラーバランスの変更、ぼかしなどを含むさまざまな効果を視覚要素に適用できます。

シェーダーが恐ろしく複雑なものであると思われる場合は、完全に同意します。 最近まで、私はgame-devに行かない限り、私の人生で彼らに出会うことはないと確信していました。 しかし、それらは応用プログラミングでも役立つことがわかった。 この場合、シェーダーを作成するための特殊な言語( GLSLHLSLなど)を知る必要はありません 長い間、インターネットにはシェーダーの既製の例が数多くあり、そのうちの1つを使用しました。 ZoomBlurEffectと呼ばれ、無料のShazzam Shader Editorのデモシェーダーの供給に含まれています。 彼のコードは次のとおりです。



ZoomBlurEffect.fx
/// <class>ZoomBlurEffect</class> /// <description>An effect that applies a radial blur to the input.</description> sampler2D inputSource : register(S0); /// <summary>The center of the blur.</summary> float2 Center : register(C0); /// <summary>The amount of blur.</summary> float BlurAmount : register(C1); float4 main(float2 uv : TEXCOORD) : COLOR { float4 c = 0; uv -= Center; for (int i = 0; i < 15; i++) { float scale = 1.0 + BlurAmount * (i / 14.0); c += tex2D(inputSource, uv * scale + Center); } c /= 15; return c; }
      
      







このシェーダーが記述されているHLSL言語を知らなくても、その操作のアルゴリズムを簡単に理解できます:最終画像の各ポイントについて、このポイントとC0レジスタに保存されたブラーセンターとの間のラインにある15ポイントの平均色値が計算されます。 このポイントからの平均ポイントの距離は、C1レジスタに保存されているBlurAmountパラメーターによって制御されます。 この例では、画像の中心からぼかしが発生するため、C0は(0.5; 0.5)であり、BlurAmountパラメーターの値は、現在のフレームが前のフレームとどれだけ異なるかによって異なりますが、後でさらに異なります。

もちろん、このフォームでシェーダーを使用することはできません。DirectXSDKの一部であるfxc.exeユーティリティを使用してコンパイルする必要があります。 ピクセルシェーダーをコンパイルした結果、拡張子が「.ps」のファイルが作成され、WPFアプリケーションで使用できます。 これを行うには、リソースとしてプロジェクトに追加し、ZoomBlurEffectクラスを作成します。



ZoomBlurEffect.cs
  /// <summary>An effect that applies a radial blur to the input.</summary> public class ZoomBlurEffect : ShaderEffect { public static readonly DependencyProperty InputProperty = RegisterPixelShaderSamplerProperty("Input", typeof (ZoomBlurEffect), 0); public static readonly DependencyProperty CenterProperty = DependencyProperty.Register("Center", typeof (Point), typeof (ZoomBlurEffect), new UIPropertyMetadata(new Point(0.9D, 0.6D), PixelShaderConstantCallback(0))); public static readonly DependencyProperty BlurAmountProperty = DependencyProperty.Register("BlurAmount", typeof (double), typeof (ZoomBlurEffect), new UIPropertyMetadata(0.1D, PixelShaderConstantCallback(1))); public ZoomBlurEffect() { PixelShader = new PixelShader { UriSource = new Uri(@"pack://application:,,,/ZoomBlurEffect.ps", UriKind.Absolute) }; UpdateShaderValue(InputProperty); UpdateShaderValue(CenterProperty); UpdateShaderValue(BlurAmountProperty); } public Brush Input { get { return ((Brush) (GetValue(InputProperty))); } set { SetValue(InputProperty, value); } } /// <summary>The center of the blur.</summary> public Point Center { get { return ((Point) (GetValue(CenterProperty))); } set { SetValue(CenterProperty, value); } } /// <summary>The amount of blur.</summary> public double BlurAmount { get { return ((double) (GetValue(BlurAmountProperty))); } set { SetValue(BlurAmountProperty, value); } } }
      
      





(実際、Shazzam Shader Editor自体は、私が使用したシェーダーのラッパークラスを生成できます。)



ウィンドウアニメーション


視覚要素にはRenderTransformプロパティがあります。これは、グラフィックサブシステムがレンダリング中に要素を変換するために使用します。 このような変換には、スケーリング、回転、傾斜が含まれます。 この例では、ウィンドウのコンテンツのスケールをゼロ(コンテンツをポイントに「最小化」)から1(コンテンツをウィンドウ全体に拡大)に変更します。 この場合、ウィンドウ自体の背景は透明になり、クロム(タイトル付きのフレーム)のレンダリングは無効になります。

WPFは従来、アニメーションにいわゆる「スムージング関数」を使用しています。 定義済みの関数を使用するか、 EasingFunctionBaseクラスから継承した独自の関数を作成できます。

この記事の例では、ElasticEase関数を使用しました。この関数は、ウィンドウに「リリースされたスプリング」の効果を与えます。最初は、確立されたサイズをわずかに超えるサイズに急激に拡張し、その後徐々に減少します。



モーションブラーエフェクトなしでウィンドウの外観をアニメーション化するための擬似コード
 double t = 0.0; int  = ; while (t < 1.0) { (ElasticEase(t)); t = ( - ) / ; } (1.0);
      
      







ここで、tは0から1まで変化します。0はアニメーションが開始する瞬間、1はアニメーションが終了する瞬間です。 ElasticEase(t)関数の値は、ほぼこの法則に従って変化します。









モーションブラーをアニメーションに追加します。 これを行うには、ウィンドウの子コントロールのEffectプロパティを使用します。

 content.Effect = new ZoomBlurEffect { Center = new Point(0.5, 0.5) };
      
      





モーションブラーアニメーションの擬似コード
 double t = 0.0; double prevEase = 0.0; int  = ; (new ZoomBlurEffect { Center = new Point(0.5, 0.5) }); while (t < 1.0) { var ease = ElasticEase(t); (ease); content.Effect.BlurAmount = ease - prevEase; prevEase = ease; t = ( - ) / ; } (1.0); (null);
      
      







このコードと前のコードの違いは、ElasticEase関数の現在の値が前のステップの値とどれだけ異なるかに応じて、各ステップでBlurAmount値を変更することです。 アニメーションの開始時には、関数は急速に成長し、BlurAmountは非常に重要であるため、ウィンドウの「ぼかし」は大きくなります。 最終的に、BlurAmountは実質的にゼロになります。つまり、「ぼかし」はほとんど目立ちません。



欠点について


残念ながら、WPFアプリケーションでモーションブラーエフェクトを適用すると、いくつかの問題が発生します。 それらのいくつかを次に示します。





おわりに


残念なことに、モーションブラーエフェクトを使用するというアイデアを製品コードの状態に持ち込むことはありませんでした。これは、対処しなければならないアプリケーションではほとんど適用できないからです。 いわば、魂のためにしたのです。 この資料が彼の作品の誰かに役立つことを願っています。



ここからデモプロジェクトをダウンロードしてください: github.com/misupov/motion-blur



All Articles