mikeo_410


HLSLでEffect

1.HLSLファイル

  効果に応じて3つのFXファイルを作りました。main()は、1ピクセルごとエントリーされるようです。uvに位置がセットされます。tex2D()で指定した任意の位置の1ピクセル(色)が取得できます。この関数の戻り値がuvの位置の新たな色になります。

  1. Grayscale.fx
    1. sampler2D input : register(s0); 
    2. float4 main(float2 uv : TEXCOORD) : COLOR 
    3. { 
    4.     float4 pix; 
    5.     pix = tex2D(input , uv.xy);
    6.     pix.rgb = (pix.r+pix.g+pix.b)/3.0f; 
    7.     return pix; 
    8. }
     
  2. Laplace.fx
    1. float2 texSize : register(c0);
    2. sampler2D input : register(s0); 
    3. float2 nextXY(float2 uv, float dx, float dy)
    4. {
    5.     uv.x += dx/texSize.x;
    6.     uv.y += dy/texSize.y;
    7.     return uv;
    8. }
    9. float4 main(float2 uv : TEXCOORD) : COLOR 
    10. { 
    11.     float4 sum; 
    12.     sum = tex2D(input, nextXY(uv,-1,-1))
    13.         + tex2D(input, nextXY(uv,0,-1))
    14.         + tex2D(input, nextXY(uv,1,-1))
    15.         + tex2D(input, nextXY(uv,-1,0))
    16.         + tex2D(input, nextXY(uv,1,0))
    17.         + tex2D(input, nextXY(uv,-1,1))
    18.         + tex2D(input, nextXY(uv,0,1))
    19.         + tex2D(input, nextXY(uv,1,1))
    20.         + (tex2D(input, uv.xy) * (-8.0f));
    21.     sum.a=1;
    22.     return sum; 
    23. }
     
  3. WaveEffect.fx
    1. float2 texSize : register(c0);
    2. sampler2D input : register(s0); 
    3. float4 main(float2 uv : TEXCOORD) : COLOR 
    4. { 
    5.     float4 pix; 
    6.     uv.x += sin(                               //sinでゆれを作る。
    7.                 (6.283/(texSize.y/5))*(1-uv.y) //縦の1/5で1周するような角度を元にyに応じて小さくしていく。 
    8.                 * ((1-uv.y)*texSize.y) )       //これにyの位置に応じた倍率を掛ける。
    9.           * (1+uv.y)*0.03;//横の幅の3->6%を移動。
    10.     pix = tex2D(input, uv.xy);
    11.     return pix; 
    12. }
     

2.HLSLのコンパイル

  Visual Studio Expressでコンパイルする方法がわからなかったのでDOS窓で行いました。 

  1. fxc /T ps_2_0 /E main /Fo"Grayscale.ps" "Grayscale.fx"
  2. fxc /T ps_2_0 /E main /Fo"Laplace.ps" "Laplace.fx"
  3. fxc /T ps_2_0 /E main /Fo"WaveEffect.ps" "WaveEffect.fx"

   DirextX SDK のインストールディレクトリの Utilities\bin\ に fxc.exe があります。
   同じ場所に dx_setenv.cmd があるので、DOS窓を開いたら最初にこれを実行しておきます。
   コンパイルの結果、Grayscale.ps、Laplace.ps、WaveEffect.ps の3つの .ps ファイルができます。

3.ShaderEffectクラス

  ShaderEffectクラスの派生クラスを作ります。
  コンストラクタで PixelShader プロパティを設定します。
  PixelShader の UriSource に .ps ファイルのパスを設定します。

  1. using System;
  2. using System.Windows;
  3. using System.Windows.Media.Effects;
  4. namespace wpf_effect1
  5. {
  6.     class Grayscale : ShaderEffect
  7.     {
  8.         public Grayscale()
  9.         {
  10.             PixelShader = new PixelShader();
  11.             string path = System.IO.Path.GetFullPath(©"..\..\Grayscale.ps");
  12.             PixelShader.UriSource = new Uri(path);
  13.         }
  14.     }
  15. }

  この例ように、単に UriSource を設定するだけなら派生クラスを作る必要はないようです。
  HLSLの記述した処理に、画像のサイズなどの値を渡すためには、派生が必要です。
  Laplace.fx にある texSize は、次のようにして設定してあります。

  1. public Size TexSize
  2. {
  3.     get { return (Size)GetValue(TexSizeProperty); }
  4.     set { SetValue(TexSizeProperty, value); }
  5. }
  6. public static readonly DependencyProperty TexSizeProperty
  7.     = DependencyProperty.Register("TexSize", typeof(Size), typeof(Laplace),
  8.             new UIPropertyMetadata(new Size(8, 8), PixelShaderConstantCallback(0)));

 

4.Effectの適用

  次のようなXAMLで表示のレイアウトをしてあります。4箇所に同じ画像を割付け、原図以外には、grayscale1、laplace1、waveEffect1と名前を付けてあります。

  1. <Window x:Class="wpf_effect1.Window1"
  2.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.     Title="HLSL Effect">
  5.     <Window.Resources>
  6.         <BitmapImage x:Key="original" UriSource="BegoniaEvansiana.png"/>
  7.     </Window.Resources>
  8.     <Grid Loaded="Grid_Loaded">
  9.         <Grid.ColumnDefinitions>
  10.             <ColumnDefinition Width="324"></ColumnDefinition>
  11.             <ColumnDefinition Width="324"></ColumnDefinition>
  12.         </Grid.ColumnDefinitions>
  13.         <StackPanel Grid.Column="0" Width="320">
  14.             <TextBlock>原図</TextBlock>
  15.             <Image Source="{StaticResource original}"/>
  16.             <TextBlock>グレースケール</TextBlock>
  17.             <Image x:Name="grayscale1" Source="{StaticResource original}"/>
  18.         </StackPanel>
  19.         <StackPanel Grid.Column="1" Width="320">
  20.             <TextBlock>ラプラス(8方向)</TextBlock>
  21.             <Image x:Name="laplace1" Source="{StaticResource original}"/>
  22.             <TextBlock></TextBlock>
  23.             <Image x:Name="waveEffect1" Source="{StaticResource original}"/>
  24.         </StackPanel>
  25.     </Grid>
  26. </Window>

  Grid の Loaded イベントで、image の Effect プロパティをセットしました。

  1. private void Grid_Loaded(object sender, RoutedEventArgs e)
  2. {
  3.     grayscale1.Effect = new Grayscale();
  4.     laplace1.Effect = new Laplace(laplace1.ActualWidth, laplace1.ActualHeight);
  5.     waveEffect1.Effect = new WaveEffect(waveEffect1.ActualWidth, waveEffect1.ActualHeight);
  6. }

  XAML の記述でEffectを設定する場合は、ShaderEffectクラスの派生クラスを異なるアセンブリ(DLL)にすることになります。

5.その他

5.1.気が付いたこと

  1. 古いノートパソコンではEffectが無視された。
    理由はわからない。
    ハードウエアがサポートしないときはエラーでなく、単に無視されるのでは。
  2. Visual Studio Express内でHLSLをコンパイルする方法がわからない。
  3. Debug方法がわからない。

5,2.main()について

  1. 図形のピクセルごとにエントリされる。
  2. 引数で渡される座標は出力位置と考えられる。
    main()の返した色がその位置に出力される。
  3. 座標は、図形の左上が(0,0)、右下が(1,1)になる。

5.3.HLSLについて

  1. 三角関数は弧度
  2. float2,float4 は、それぞれ2つ、4つのfloatの組


mikeo_410@hotmail.com