Vous êtes sur la page 1sur 43

HLSL Single Input Shader Effect

Part 1: Create and Implement Effect


Create a simple, single input HLSL shader Effect and utilize it in WPF application. Then use Parameters and
C# Dependency Properties to animate it.

Introduction
Windows Presentation Foundation (WPF) allows you to easily create rich, dynamic user experiences
which can make the presentation of any information more compelling. WPF users will be treated to a
plethora of new features and improvements with the release of .Net Framework 3.5 Service Pack 1! Service
Pack 1 adds support for High Level Shading Language (HLSL) shaders with the ShaderEffect class,
allowing an almost unlimited range of visual effects to be applied to WPF content. HLSL shaders are
implemented on the GPU, and thus offer significant performance and visual advantages to their softwarerendered counterparts. The lab will guide you through the development of a simple, single image input
HLSL shader effect. The objectives of this lab are as follows:
1. Teach you how to develop and compile an HLSL shader.
2. Create a WPF project which utilizes custom created shaders.
3. Animate effect by passing parameters to an HLSL program using a managed code Effect subclass.

Estimated time to complete this lab: 1 hour.

Contents
A. Develop HLSL program
1. Using Visual Studio Template
2. Without Visual Studio Template
B. Testing the HLSL program
C. Create a New WPF Application
D. Develop the Application
E. Utilize HLSL Effect in the Application

F. Animate Shader Effect with Parameters

Prerequisites
The following are required to run this Mini-Lab:
Windows Vista; Windows XP; Windows Server 2003; Windows Server 2008
Microsoft .Net Framework 3.5 Service Pack 1 (230 MB)
Visual Studio 2008 Express (C#) (35-70 MB)
OR
Visual Studio 2008 Professional Edition (3389.6 MB)
DirectX SDK (458 MB)
Shader Effect Visual Studio Project Template and Build Task (optional but highly recommended)
HLSL Test Environment: HLSLTester (optional)
IMPORTANT: The steps in this lab are intended to provide an overview of the technology presented. They
are not intended to, and may not follow Microsoft best practices or guidance on the technology presented.

A1. Develop HLSL Program Using Visual Studio Template


1. Launch Visual Studio 2008
a. Start All Programs Microsoft Visual Studio 2008 Microsoft Visual Studio 2008
2. Start a New Project
a. File New Project

b. Select .Net Framework 3.5 from the drop-down menu on the upper right-hand side. Under
the Project Types left-hand list select Visual C# Windows. Under the Templates righthand list select WPF Shader Effect Library from My Templates section. Name the Project
ShaderEffectLibrary.

3. Select the Load project normally radio button on the Security Warning dialogue window.

4. Once the Visual Studio project is open, change the name of the .fx and .cs files from Effect1 to
WaveEffect. Open the WaveEffect.fx file.

5. Select all the existing text and delete it. We need to retrieve the screen (image) color information to
be used by our shader effect. Add an HLSL parameter of type sampler2D named Input, which will
use value present in a register s0, to WaveEffect.fx:
sampler2DInput:register(s0);

6. Add the main function header for the program. It will take as input a vector containing 2 floats
named uv corresponding to the x and y coordinate values of a pixel and output a vector
containing 4 floats corresponding to the color composition values (Red, Green, Blue, and Alpha) of
that pixel. Copy and paste the highlighted code into WaveEffect.fx:
sampler2DInput:register(s0);
float4main(float2uv:TEXCOORD):COLOR
{}

7. The HLSL program will apply an arithmetic operation to all of the pixels of an arbitrary image. We
wish to use an arithmetic operation which will reposition each pixel in a repetitive wave pattern and
produce a sort of wave distortion effect. In arithmetic, the outputs of the sine and cosine functions
produce graphical wave patterns which oscillate values from -1 to 1. We will utilize these arithmetic
functions to calculate the new position of our pixels. Also, the sine and cosine functions map to the
x and y component values of a coordinate respectively; this is important because we will calculate
the new value of a pixels x coordinate using the pixels y component as an offset and vice-versa for
the pixels y coordinate. We can also dictate the length (distance from wave peak to dip) and width
(peak to peak). We will use the values 200 for length and 0.01 for width. Copy and paste the
highlighted code inside the main function:
sampler2DInput:register(s0);
float4main(float2uv:TEXCOORD):COLOR
{
uv.y=uv.y+(sin((uv.x)*200)*0.01);
uv.x=uv.x+(cos((uv.y)*200)*0.01);
}

8. To complete our program we must return a value, more specifically a 4 float vector representing the
color composition of a particular pixel. To do so, add a variable of type float4 called Color. Set the
value of the variable such that the color of the pixel at the original uv.x and uv.y coordinates is the
color of the pixel at the newly calculated uv.x and uv.y coordinates. This can be done by using the
HLSL tex2D function, passing the input sampler state (Input) and input texture coordinates (uv) as
arguments. The tex2D function will return a 4 float vector representing the color composition of the
pixel located at the texture coordinates, represented by the 2 float vector argument passed, of the
sampler state. Once Color has been set to the value at the calculated position, return it so that the
pixel at the original uv.xy will have a new color composition. Copy and paste the highlighted code
inside the main function:
sampler2DInput:register(s0);
float4main(float2uv:TEXCOORD):COLOR
{
float4Color;
uv.y=uv.y+(sin((uv.x)*200)*0.01);

uv.x=uv.x+(cos((uv.y)*200)*0.01);
Color=tex2D(Input,uv.xy);
returnColor;
}

9. Now lets add a C# wrapper for our HLSL program


10.
Open the WaveEffect.cs file from the solution explorer on the right-hand side.

11.
Select all the existing text and delete it. Copy and paste the following. Generally speaking, this
managed code will set-up a Dependency Property used to pass the screen color information to our
HLSL shader and creates a URI for referencing the bytecode. For a more detailed explanation,
consult this blog entry.
using System;
using System.Windows;

using System.Windows.Media;
using System.Windows.Media.Effects;
namespace ShaderEffectLibrary
{
public class WaveEffect : ShaderEffect
{
static WaveEffect()
{
_pixelShader.UriSource = Global.MakePackUri("WaveEffect.ps");
}
public WaveEffect()
{
this.PixelShader = _pixelShader;

// Update each DependencyProperty that's registered with a shader register.


// is needed to ensure the shader gets sent the proper default value.
UpdateShaderValue(InputProperty);

This

public Brush Input


{
get { return (Brush)GetValue(InputProperty); }
set { SetValue(InputProperty, value); }
}
// Brush-valued properties turn into sampler-property in the shader.
// This helper sets "ImplicitInput" as the default, meaning the default
// sampler is whatever the rendering of the element it's being applied to is.
public static readonly DependencyProperty InputProperty =
ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(WaveEffect), 0);
private static PixelShader _pixelShader = new PixelShader();
}

12.
Now we will test our HLSL file to ensure it works. The following steps in section B briefly
instruct you on how to test HLSL for compilation errors using the HLSLTester application. Section B
is optional; proceed to section C if you wish to skip it.

A2. Develop HLSL Program Without Visual Studio Template


1. Open a text editor, Notepad will be used for this example
a. Start All Programs Accessories Notepad

2. We need to retrieve the screen (image) color information to be used by our shader effect. Add HLSL
parameter of type sampler2D named Input which will use value present in a register s0 to
notepad document:
sampler2DInput:register(s0);

3. Add the main function header for the program. It will take as input a vector containing 2 floats
named uv corresponding to the x and y coordinate values of a pixel and output a vector
containing 4 floats corresponding the color composition values (Red, Green, Blue, and Alpha) of
that pixel. Copy and paste the highlighted code into your document:
sampler2DInput:register(s0);
float4main(float2uv:TEXCOORD):COLOR
{}

4. The HLSL program will apply an arithmetic operation to all of the pixels of an arbitrary image. We
wish to use an arithmetic operation which will reposition each pixel in a repetitive wave pattern and
produce a sort of wave distortion effect. In arithmetic, the outputs of the sine and cosine functions
produce graphical wave patterns which oscillate values from -1 to 1. We will utilize these arithmetic

functions to calculate the new position of our pixels. Also, the sine and cosine functions map to the
x and y component values of a coordinate respectively; this is important because we will calculate
the new value of a pixels x coordinate using the pixels y component as an offset and vice-versa for
the pixels y coordinate. We can also dictate the length (distance from wave peak to dip) and width
(peak to peak). We will use the values 200 for length and 0.01 for width. Copy and paste the
highlighted code inside the main function:
sampler2DInput:register(s0);
float4main(float2uv:TEXCOORD):COLOR
{
uv.y=uv.y+(sin((uv.x)*200)*0.01);
uv.x=uv.x+(cos((uv.y)*200)*0.01);
}

5. To complete our program we must return a value, more specifically a 4 float vector representing the
color composition of a particular pixel. To do so, add a variable of type float4 called Color. Set the
value of the variable such that the color of the pixel at the original uv.x and uv.y coordinates is the
color of the pixel at the newly calculated uv.x and uv.y coordinates. This can be done by using the
HLSL tex2D function, passing the input sampler state (Input) and input texture coordinates (uv) as
arguments. The tex2D function will return a 4 float vector representing the color composition of the
pixel located at the texture coordinates, represented by the 2 float vector argument passed, of the
sampler state. Once Color has been set to the value at the calculated position, return it so that the
pixel at the original uv.xy will have a new color composition. Copy and paste the highlighted code
inside the main function:
sampler2DInput:register(s0);
float4main(float2uv:TEXCOORD):COLOR
{
float4Color;
uv.y=uv.y+(sin((uv.x)*200)*0.01);
uv.x=uv.x+(cos((uv.y)*200)*0.01);
Color=tex2D(Input,uv.xy);
returnColor;
}

6. Now that we have finished writing our HLSL code for the effect, we will now compile it into bytecode
that our WPF app can use. For this part of the lab you must have the DirectX SDK installed.
7. Save the HLSL code written in Notepad with as WaveEffect.fx to an arbitrary location. Make sure to
remember this location, as you will have to reference it later.
8. Open the command line editor
a. Start Run
b. In the Open: text field type cmd, and select OK:

9. Navigate to the location where you saved the .fx HLSL file previously
10.
Now we will use fxc.exe available through the DirectX SDK to compile our HLSL code. This is
accomplished using the following command line argument:
c. fxc /T ps_2_0 /E main /Fo<name or HLSL file>.ps <name of HLSL file>.fx
11.
This command says to compile to the Pixel Shader 2.0 profile, and to look for the entrypoint
named "main".
12.
If the compilation is successful, you will receive a message indicating success as well as the
location of a newly created .ps file. The following is a sample image of the command and success
message assuming the file was named WaveEffect.fx:

13.
Now that we have produced the pixel shader bytecode, lets create a visual studio project for
our shader effect. Start Visual Studio.
14.
Go to File New Project and under NET Framework 3.5 and select the C# class
library project template.
15.
Enter ShaderEffectLibrary as the Name of the project.

16.
Now lets add the WaveEffect.fx and WaveEffect.ps files that we created earlier to our
project. Right-click on the ShaderEffectLibrary project icon.
d. Add Existing Item

17.
Navigate to the WaveEffect.fx and WaveEffect.ps files and select them to be added. Also
rename the Class1.cs file to WaveEffect.cs.
18.
Select the WaveEffect.ps file in the Solution Explorer window. Set the Build Action of the file
to Resource.

19.
Lets add a class to facilitate adding the bytecode into the build component. We will use
pack:// URI to have the bytecode live in same packaging as the C#. Greg Schechters blog does a
good job of explaining this.
20.
Right-click on the ShaderEffectLibrary project icon.
e. Add New Item
21.
When the Add New Item Dialogue box appears select the Code option under Visual C# Items
in the Categories: window. Select the Class template and name it EffectLibrary.cs.

22.
Right-click on the ShaderEffectLibrary project icon and select Add Reference.
23.
Select the .NET tab and select PresentationCore, PresentationFramework, and
WindowsBase.

24.
Open the EffectLibrary.cs file. Select all of the contents inside the class and delete them.
Copy and paste the following code for creating a URI for bytecode:
using System;
using System.Windows;
using System.Reflection;
namespace ShaderEffectLibrary
{
internal static class Global
{
public static Uri MakePackUri(string relativeFile)
{
string uriString = "pack://application:,,,/" + AssemblyShortName + ";component/" +
relativeFile;
return new Uri(uriString);

}
private static string AssemblyShortName
{
get
{
if (_assemblyShortName == null)
{
Assembly a = typeof(Global).Assembly;
// Pull out the short name.
_assemblyShortName = a.ToString().Split(',')[0];
}
}

return _assemblyShortName;

}
}

private static string _assemblyShortName;

25.
Now lets add a C# wrapper for our HLSL program
26.
Open the WaveEffect.cs file from the solution explorer on the right-hand side.
27.
Select all the existing text and delete it. Copy and paste the following. Generally speaking, this
managed code will set-up a Dependency Property used to pass the screen color information to our
HLSL shader and calls the method we just added in EffectLibrary.cs to create a URI for
referencing the bytecode. For a more detailed explanation, consult Greg Schechters blog entry.
using
using
using
using

System;
System.Windows;
System.Windows.Media;
System.Windows.Media.Effects;

namespace ShaderEffectLibrary
{
public class WaveEffect : ShaderEffect
{
static WaveEffect()
{
_pixelShader.UriSource = Global.MakePackUri("WaveEffect.ps");
}
public WaveEffect()
{
this.PixelShader = _pixelShader;

// Update each DependencyProperty that's registered with a shader register.


// is needed to ensure the shader gets sent the proper default value.
UpdateShaderValue(InputProperty);

This

public Brush Input


{
get { return (Brush)GetValue(InputProperty); }
set { SetValue(InputProperty, value); }
}
// Brush-valued properties turn into sampler-property in the shader.
// This helper sets "ImplicitInput" as the default, meaning the default
// sampler is whatever the rendering of the element it's being applied to is.
public static readonly DependencyProperty InputProperty =
ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(WaveEffect), 0);
private static PixelShader _pixelShader = new PixelShader();
}
}

28.
Now we will test our HLSL file to ensure it works. The following steps in section B briefly
instruction you on how to test HLSL for compilation errors using the HLSLTester visual studio project.
Section B is optional; proceed to section C if you wish to skip it.

B. Testing the HLSL Program (Optional)


1. Using the HLSLTester link presented earlier in the document download HLSLTester.zip available near
the bottom of the page:

2. Once you have finished saving the zip file, unzip the contents to a location of your preference.
3. Navigate to and open the HLSLTester Executable Application:
a. HLSLTester HLSLTester Bin Debug HLSLTester (Application)

4. We can now load an image with which to test our HLSL Program. Select the Open button in the
upper left-hand corner of the HLSLTester window. Navigate to any image you would like to use as
your test subject. We will use the Toco Toucan image available in the sample pictures folder on
most windows vista platforms:

5. Now that we have an image with which to test our code, we can simply paste our HLSL code in place
of the sample code in the lower left-hand text area of the window. If you have followed the steps
exactly you should not receive any compilation errors or warnings in the lower right-hand text area
and your image should exhibit the wave distortion effect described earlier:

6. The HLSLTester is useful for doing some simple debugging before attempting to compile your HLSL
into bytecode. Once you become more comfortable developing HLSL code you may find this tool to
be unnecessary or even find a better tool altogether.

C. Create a New WPF Application


1. Start Visual Studio
2. Go to File New Project and under NET Framework 3.5 choose WPF Application.
3. Enter HLSLEffectLab as the Name of your project.

D. Develop the Application


1. We will now develop a simple WPF application that will utilize our HLSL effect. The app will we
will build will display a panel of images. When the mouse enters the area of a particular image, it
will display in the upper-half of our app window and apply our HLSL effect to the image to create the
illusion that the image is waving on the screen.
2. Lets start by creating the app without the HLSL effect. This will basically be the same app as
described above, excluding the waving effect being applied once an image has the mouse enter its
area.
3. We must first add the images we will use to the Visual Studio project. Right-click on the
HLSLEffectLab project icon.
a. Add New Folder

4. Name the Folder Images


5. Right-click on the Images folder.
a. Add Existing Item

6. Navigate to where the windows sample images are stored and select the Desert Landscape,
Dock, Green Sea Turtle, Humpback Whale, Oryx Antelope, and Waterfall images to be added
to the folder.
7. The files representing the images we will use should now be present if you expand the Flags folder.
8. Now lets add the XAML for our app. Open the Window1.xaml file and select all the existing text

and delete it. Copy and paste the following:


<Window x:Class="HLSLEffectLab.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Waving Image" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>

</Grid.RowDefinitions>
<Image Name="MainImage" Stretch="Fill" Grid.Row="0"/>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image Name="Image1" Source="Images/Desert Landscape.jpg" Stretch="UniformToFill"
Grid.Column="0" Grid.Row="1"/>
<Image Name="Image2" Source="Images/Dock.jpg" Stretch="UniformToFill"
Grid.Column="1" Grid.Row="1"/>
<Image Name="Image3" Source="Images/Green Sea Turtle.jpg" Stretch="UniformToFill"
Grid.Column="2" Grid.Row="1"/>
<Image Name="Image4" Source="Images/Humpback Whale.jpg" Stretch="UniformToFill"
Grid.Column="3" Grid.Row="1"/>
<Image Name="Image5" Source="Images/Oryx Antelope.jpg" Stretch="UniformToFill"
Grid.Column="4" Grid.Row="1"/>
<Image Name="Image6" Source="Images/Waterfall.jpg" Stretch="UniformToFill"
Grid.Column="5" Grid.Row="1"/>
</Grid>
</Grid>
</Window>

9. This XAML code places a grid in the parent Window. That Grid is divided into two equally sized
rows: the first row contains an Image named MainImage and the second row contains another
Grid. The second Grid is divided into six equally sized columns each containing an image formatted
to uniformly fill the area of that column. The resulting app window should looks as follows.

10.
Next lets and the functionality to make MainImage use the image our mouse is over as its
source. If you examine the XAML from step 8, you will notice that the images placed in the six
columns of the inner most Grid have their Source properties set. We did not set the Source of
MainImage because, as described earlier, we want MainImage to take on the source of the image
whose area our mouse has entered. This means we must functional logic to the code-behind file for
our XAML. Because we want this logic to execute whenever our mouse enters one of the images
area we must set a routine to be called when the MouseEnter action occurs for each image. Add
the highlighted XAML to Window1.xaml:
...
<Image Name="Image1" Source="Flags/Desert Landscape.jpg" Stretch="UniformToFill" Grid.Column="0"
Grid.Row="1" MouseEnter="Image_MouseEnter"/>
<Image Name="Image2" Source="Flags/Dock.jpg" Stretch="UniformToFill" Grid.Column="1"
Grid.Row="1" MouseEnter="Image_MouseEnter"/>
<Image Name="Image3" Source="Flags/Green Sea Turtle.jpg" Stretch="UniformToFill" Grid.Column="2"
Grid.Row="1" MouseEnter="Image_MouseEnter"/>
<Image Name="Image4" Source="Flags/Humpback Whale.jpg" Stretch="UniformToFill" Grid.Column="3"
Grid.Row="1" MouseEnter="Image_MouseEnter"/>
<Image Name="Image5" Source="Flags/Oryx Antelope.jpg" Stretch="UniformToFill" Grid.Column="4"
Grid.Row="1" MouseEnter="Image_MouseEnter"/>
<Image Name="Image6" Source="Flags/Waterfall.jpg" Stretch="UniformToFill" Grid.Column="5"
Grid.Row="1" MouseEnter="Image_MouseEnter"/>
...

11.
In the Solution Explorer window, expand the Window1.xaml file to reveal the
Window1.xaml.cs file. Open Window1.xaml.cs.
12.
We want to add logic that will set the source of MainImage to be the source of whichever
image calls the Image_MouseEnter routine. Select all the existing text and delete it. Copy and
paste the following C# code:

using
using
using
using
using
using
using
using
using
using
using

System;
System.Collections.Generic;
System.Linq;
System.Text;
System.Windows;
System.Windows.Controls;
System.Windows.Data;
System.Windows.Documents;
System.Windows.Input;
System.Windows.Media;
System.Windows.Media.Imaging;

using System.Windows.Navigation;
using System.Windows.Shapes;
namespace HLSLEffectLab
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Image_MouseEnter(object sender, MouseEventArgs e)
{
MainImage.Source = ((Image)sender).Source;
}
}
}

13.
The app should now reflect the added functionality. If we place our mouse in the area of the
green sea turtle (third image from left) the resulting app window should look as follows:

14.

Our app is almost complete. Now we will add the HLSL effect to be applied to MainImage.

E. Utilize HLSL Effect in the Application


1. Now that we have developed an HLSL effect and a simple WPF application we can utilize our effect
to be used in the app. Our goal is to animate the image displayed in the upper-half of the window
when its source is set with our HLSL effect and create the illusion that the image is waving on the
screen. In this section we will simply apply our wavy effect to MainImage without animation. We
will implement the animation in the next and final section.
2. Let start by adding our HLSL effect project to the current solutions project space.
3. Right-click on Solution HLSLEffectLab (1 Project)
a. Add Existing Project

4. Navigate to the ShaderEffectLibrary project and, if prompted, select the Load project normally
radio button on the Security Warning dialogue box. The ShaderEffectLibrary project should now
be viewable as an extension of the current solution.
5. We must now add the ShaderEffectLibrary project as a reference to the HLSLEffectLab project.
6. Right-click on the HLSLEffectLab project icon and select Add Reference

7. Select the Projects tab and select ShaderEffectLibrary.

8. Now we must add lines to the C# and XAML in order to begin using ShaderEffectLibrary in our
code. Add the following lines to the namespace declarations of Window1.xaml:
<Window x:Class="HLSLEffectLab_.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:eff="clr-namespace:ShaderEffectLibrary;assembly=ShaderEffectLibrary"
Title="Waving Flag" Height="300" Width="300">
...

9. Add the following lines to the using directives of Window1.xaml.cs:


using
using
using
using
using

System;
System.Collections.Generic;
System.Linq;
System.Text;
System.Windows;

using
using
using
using
using
using
using
using
using
using
...

System.Windows.Controls;
System.Windows.Data;
System.Windows.Documents;
System.Windows.Input;
System.Windows.Media;
System.Windows.Media.Imaging;
System.Windows.Navigation;
System.Windows.Shapes;
System.Windows.Media.Effects;
ShaderEffectLibrary;

10.
We are now completely set-up to utilize our HLSLEffect in the app. Now we can add logic so
as to execute our effect whenever the mouse enters the area of any image. Modify the
Image_MouseEnter method by adding the highlighted logic to Window1.xaml.cs:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Image_MouseEnter(object sender, MouseEventArgs e)
{
MainImage.Source = ((Image)sender).Source;
MainImage.Effect = new ShaderEffectLibrary.WaveEffect();
}
}

11.
The app should now reflect the added functionality. If we place our mouse in the area of the
green sea turtle (third image from left) the resulting app window should look as follows:

12.

Now we will add the final piece of functionality to our app, animating the HLSL effect.

F. Animate HLSL Effect with Parameters


1. To animate MainImage we will utilize the WPF animation system to animate a dependency property
of a framework element. We will modify the Effect property of Image. Add the highlighted code to
Window1.xaml:
</Grid.RowDefinitions>
<ImageName="MainImage"Stretch="Fill"Grid.Row="0">
<Image.Effect>
<eff:WaveEffectx:Name="waveEff"/>
</Image.Effect>
</Image>
<GridGrid.Row="1">

2. We must add a new using directive to take utilize the WPF animation system, add the highlighted
line to the using directives of Window1.xaml.cs:
...
usingSystem.Windows.Input;
usingSystem.Windows.Media;

usingSystem.Windows.Media.Effects;
usingSystem.Windows.Media.Imaging;
usingSystem.Windows.Media.Animation;
usingSystem.Windows.Navigation;
...

3. Next we will add a method to perform the animation. Add the following method to
Window1.xaml.cs:
privatestaticvoidDoubleAnimHelper(IAnimatablesubject,DependencyPropertydp,doublefrom,
doubleto,intseconds)
{
DoubleAnimationtimeline=null;
timeline=newDoubleAnimation(from,to,newDuration(newTimeSpan(0,0,seconds)))
{
AutoReverse=false
};
subject.BeginAnimation(dp,timeline);
}

4. As mentioned earlier, our plan is to animate a dependency property. Currently our HLSL effect has
no parameter and dependency property with which to do so; lets add one now. Add the highlighted
code to WaveEffect.fx and WaveEffect.cs respectively (changes to the expressions calculating
uv.y and uv.x were made to include the new parameter and produce a more fluid wave effect using
new constant values). If you chose not to use the shader effect project template and build task, you
will need to re-compile WaveEffect.fx file the is part of the ShaderEffectLibrary project as
detailed in section A-2:
...
sampler2DInput:register(s0);
floatfrequency:register(C0);
float4main(float2uv:TEXCOORD):COLOR
{
float4Color;
uv.y=uv.y+(sin((uv.x+frequency)*17.54389695)*0.004210526);
uv.x=uv.x+(cos((uv.y+frequency)*17.54389695)*0.004210526);
Color=tex2D(implicitInput,uv.xy);
returnColor;

}
...
...
publicWaveEffect()
{
this.PixelShader=_pixelShader;
//UpdateeachDependencyPropertythat'sregisteredwithashaderregister.This
//isneededtoensuretheshadergetssenttheproperdefaultvalue.
UpdateShaderValue(InputProperty);
UpdateShaderValue(WavinessProperty);
}
...
...
...
publicdoubleWaviness
{
get{return(double)GetValue(WavinessProperty);}
set{SetValue(WavinessProperty,value);}
}
publicstaticreadonlyDependencyPropertyWavinessProperty=
DependencyProperty.Register("Waviness",typeof(double),typeof(WaveEffect),new
UIPropertyMetadata(0.0,PixelShaderConstantCallback(0)));
...

5. Now we must only make a small modification to Window1.xaml.cs and we will be able to animate
our effect. Modify the Image_MouseEnter method by removing the green highlighted code and
adding the yellow highlighted code:
...
privatevoidImage_MouseEnter(objectsender,MouseEventArgse)
{
MainImage.Source=((Image)sender).Source;
MainImage.Effect=newShaderEffectLibrary.WaveEffect();
DoubleAnimHelper(waveEff,WaveEffect.WavinessProperty,0.3,0.3,3);
}

6. Your resulting app window should now exhibit the functionality described at the beginning of the lab.
If you had any issues with developing any of the four files (five if you chose not to use the shader
effect project template and build task), a complete copy of each is attached at the end.

Window1.xaml
<Window x:Class="HLSLEffectLab_.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:eff="clr-namespace:ShaderEffectLibrary;assembly=ShaderEffectLibrary"
Title="Waving Image" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Image Name="MainImage" Stretch="Fill" Grid.Row="0">
<Image.Effect>
<eff:WaveEffect x:Name="waveEff"/>
</Image.Effect>
</Image>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image Name="Image1" Source="Images/Desert Landscape.jpg" Stretch="UniformToFill"
Grid.Column="0" Grid.Row="1" MouseEnter="Image_MouseEnter"/>
<Image Name="Image2" Source="Images/Dock.jpg" Stretch="UniformToFill" Grid.Column="1"
Grid.Row="1" MouseEnter="Image_MouseEnter"/>
<Image Name="Image3" Source="Images/Green Sea Turtle.jpg" Stretch="UniformToFill"
Grid.Column="2" Grid.Row="1" MouseEnter="Image_MouseEnter"/>
<Image Name="Image4" Source="Images/Humpback Whale.jpg" Stretch="UniformToFill"
Grid.Column="3" Grid.Row="1" MouseEnter="Image_MouseEnter"/>
<Image Name="Image5" Source="Images/Oryx Antelope.jpg" Stretch="UniformToFill"
Grid.Column="4" Grid.Row="1" MouseEnter="Image_MouseEnter"/>
<Image Name="Image6" Source="Images/Waterfall.jpg" Stretch="UniformToFill" Grid.Column="5"
Grid.Row="1" MouseEnter="Image_MouseEnter"/>
</Grid>
</Grid>
</Window>

Window1.xaml.cs

using
using
using
using
using
using
using
using
using
using
using
using
using
using
using
using

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

namespace HLSLEffectLab_
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Image_MouseEnter(object sender, MouseEventArgs e)
{
MainImage.Source = ((Image)sender).Source;
DoubleAnimHelper(waveEff, WaveEffect.WavinessProperty, -0.3, 0.3, 3);
}
private static void DoubleAnimHelper(IAnimatable subject, DependencyProperty dp, double from,
double to, int seconds)
{
DoubleAnimation timeline = null;
timeline = new DoubleAnimation(from, to, new Duration(new TimeSpan(0, 0, seconds)))
{
AutoReverse = false
};
subject.BeginAnimation(dp, timeline);
}
}

WaveEffect.fx
//----------------------------------------------------------------------------------------//
// WPF ShaderEffect HLSL Template
//
//----------------------------------------------------------------------------------------//----------------------------------------------------------------------------------------// Shader constant register mappings (scalars - float, double, Point, Color, Point3D, etc.)
//----------------------------------------------------------------------------------------float frequency : register(C0);
//----------------------------------------------------------------------------------------// Sampler Inputs (Brushes, including ImplicitInput)
//----------------------------------------------------------------------------------------sampler2D implicitInput : register(S0);
//----------------------------------------------------------------------------------------// Pixel Shader
//----------------------------------------------------------------------------------------float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 Color;
uv.y = uv.y + (sin((uv.x + frequency)*17.54389695)*0.004210526);
uv.x = uv.x + (cos((uv.y + frequency)*17.54389695)*0.004210526);
Color = tex2D( implicitInput, uv.xy);
return Color;
}

WaveEffect.cs
using
using
using
using

System;
System.Windows;
System.Windows.Media;
System.Windows.Media.Effects;

namespace ShaderEffectLibrary
{
public class WaveEffect : ShaderEffect
{
#region Constructors
static WaveEffect()
{
_pixelShader.UriSource = Global.MakePackUri("WaveEffect.ps");
}

public WaveEffect()
{
this.PixelShader = _pixelShader;

// Update each DependencyProperty that's registered with a shader register.


// is needed to ensure the shader gets sent the proper default value.
UpdateShaderValue(InputProperty);
UpdateShaderValue(WavinessProperty);

This

#endregion
#region Dependency Properties
public Brush Input
{
get { return (Brush)GetValue(InputProperty); }
set { SetValue(InputProperty, value); }
}
// Brush-valued properties turn into sampler-property in the shader.
// This helper sets "ImplicitInput" as the default, meaning the default
// sampler is whatever the rendering of the element it's being applied to is.
public static readonly DependencyProperty InputProperty =
ShaderEffect.RegisterPixelShaderSamplerProperty("implicitInput", typeof(WaveEffect), 0);
public double Waviness
{
get { return (double)GetValue(WavinessProperty); }
set { SetValue(WavinessProperty, value); }
}
public static readonly DependencyProperty WavinessProperty =
DependencyProperty.Register("Waviness", typeof(double), typeof(WaveEffect), new
UIPropertyMetadata(0.0, PixelShaderConstantCallback(0)));
#endregion
#region Member Data
private static PixelShader _pixelShader = new PixelShader();
#endregion
}
}

EffectLibrary.cs

using System;
using System.Windows;
using System.Reflection;
namespace ShaderEffectLibrary_
{
internal static class Global
{
public static Uri MakePackUri(string relativeFile)
{
string uriString = "pack://application:,,,/" + AssemblyShortName + ";component/" +
relativeFile;
return new Uri(uriString);
}
private static string AssemblyShortName
{
get
{
if (_assemblyShortName == null)
{
Assembly a = typeof(Global).Assembly;

// Pull out the short name.


_assemblyShortName = a.ToString().Split(',')[0];

return _assemblyShortName;
}

private static string _assemblyShortName;


}

Vous aimerez peut-être aussi