独自のエフェクトを作成してUIElement.Effectに適用する (ShaderEffectを派生させてカスタムエフェクトを作成する) - WPF

WPFでは標準のエフェクトとして、BlurEffect, DropShadowEffectクラスが用意されています。WPFではブラーやドロップシャドウ以外の効果を与えたい場合には独自のエフェクトを作成できます。
ここでは、独自の効果を作成し、UIElement.Effectに適用する手順を紹介します。

1. ピクセルシェーダーの定義

まず、どのような効果を画面に対して適用するかを決定する必要があります。効果はピクセルシェーダーとして実装します。ピクセルシェーダーの記述にはHLSLと呼ばれる言語を用いて記述します。今回は、シンプルな例として画像に波紋の効果を与えるシェーダーを記述します。WateryEffect.fxという名前でテキストファイルを作成し、以下のコードを記述します。

コード (WateryEffect.fx)

sampler2D input : register(S0);  
float4 main(float2 uv : TEXCOORD) : COLOR  
{ 
  uv.y = uv.y + (sin(uv.y*100)*0.03);  
  return tex2D( input , uv.xy);   
}  
※WateryEffects.fxの文字コードはSHIFT-JISで記述します。

2.HLSLファイルののコンパイル

記述したWateryEffect.fxをコンパイルします。コンパイルの詳細はこちらで紹介しています。
fxc WateryEffect.fx /T ps_2_0 /Fo WateryEffect.ps
を実行します。
fxc WateryEffect.fx /T ps_3_0 /Fo WateryEffect.ps
でもよいです。(ピクセルシェーダーのバージョンが変わります。)
Microsoft (R) Direct3D Shader Compiler 9.27.952.3012
Copyright (C) Microsoft Corporation 2002-2009. All rights reserved.
 
compilation succeeded; see c:\WpfEffects\WateryEffect.ps
とメッセージが表示され、コンパイルが成功したことを確認します。
WateryEffect.fxを配置したディレクトリにWateryEffect.psファイルがあることを確認します。

3.WPFアプリケーションの実装

WPFアプリケーションを作成します。
新規プロジェクト作成で[WPFアプリケーション]を選択しWPFアプリケーションを作成します。ソリューションエクスプローラのポップアップメニューの[追加]メニューの[既存の項目]メニューから、先ほどコンパイルしたHLSLファイルとコンパイル結果のファイル(WateryEffect.fxとWateryEffect.ps)をプロジェクトに追加します。
次に、カスタムエフェクトクラスを作成します。ソリューションエクスプローラの[追加]メニューの[新しい項目]メニューを選択し[クラス]を追加します。クラス名(ファイル名)は"WateryEffect"とします。


WateryEffect.csを実装します。以下のコードを記述します。

コード (WateryEffect.cs)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Effects;

namespace WpfEffects
{
  class WateryEffect : ShaderEffect
  {
    public WateryEffect()
    {
      PixelShader ps = new PixelShader();
      string path = System.IO.Path.GetFullPath(@"WateryEffect.ps");
      ps.UriSource = new Uri(path);

      this.PixelShader = ps;
      UpdateShaderValue(InputProperty);
    }

    public Brush Input
    {
      get { return (Brush)GetValue(InputProperty); }
      set { SetValue(InputProperty, value); }
    }

    public static readonly DependencyProperty InputProperty
      = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(WateryEffect), 0);
  }
}

続いてフォームを設計します。
下図のフォームを用意しました。


アプリケーションを実装します。以下のコードを記述します。

コード (MainWindow.xaml.cs)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Media.Effects;

namespace WpfEffects
{
  public partial class MainWindow : Window
  {
    public MainWindow()
    {
      InitializeComponent();
    }

    private void button2_Click(object sender, RoutedEventArgs e)
    {
    }

    private void button3_Click(object sender, RoutedEventArgs e)
    {
      WateryEffect we = new WateryEffect();
      button1.Effect = we;
      image1.Effect = we;
    }
  }
}

4.プログラムの実行

プログラムを実行します。


button3をクリックすると以下の結果が得られます。



著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
掲載日: 2010-08-11
iPentec all rights reserverd.