build your own hlsls hader effect lab

47
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 software-rendered 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.

Upload: marijana-markov

Post on 25-Dec-2015

4 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Build Your Own Hlsls Hader Effect Lab

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 software-rendered 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.

Page 2: Build Your Own Hlsls Hader Effect Lab

Estimated time to complete this lab: 1 hour.

Contents

A. Develop HLSL program1. Using Visual Studio Template2. Without Visual Studio Template

B. Testing the HLSL programC. Create a New WPF Application

Page 3: Build Your Own Hlsls Hader Effect Lab

D. Develop the ApplicationE. Utilize HLSL Effect in the ApplicationF. 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)

ORVisual 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 2008a. Start All Programs Microsoft Visual Studio 2008 Microsoft Visual Studio 2008

2. Start a New Projecta. File New Project

Page 4: Build Your Own Hlsls Hader Effect Lab

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 right-hand list select WPF Shader Effect Library from My Templates section. Name the Project ShaderEffectLibrary.

Page 5: Build Your Own Hlsls Hader Effect Lab

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

Page 6: Build Your Own Hlsls Hader Effect Lab

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.

Page 7: Build Your Own Hlsls Hader Effect Lab

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:

sampler2D Input : 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:

sampler2D Input : register(s0);

float4 main(float2 uv : TEXCOORD) : COLOR{}

Page 8: Build Your Own Hlsls Hader Effect Lab

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 pixel’s y component as an offset and vice-versa for the pixel’s 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:

sampler2D Input : register(s0);

float4 main(float2 uv : 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:

sampler2D Input : register(s0);

float4 main(float2 uv : TEXCOORD) : COLOR{ float4 Color; 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); return Color;}

9. Now let’s add a C# wrapper for our HLSL program

Page 9: Build Your Own Hlsls Hader Effect Lab

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() {

Page 10: Build Your Own Hlsls Hader Effect Lab

_pixelShader.UriSource = Global.MakePackUri("WaveEffect.ps"); }

public WaveEffect() { this.PixelShader = _pixelShader;

// Update each DependencyProperty that's registered with a shader register. This // is needed to ensure the shader gets sent the proper default value. UpdateShaderValue(InputProperty); }

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 examplea. Start All Programs Accessories Notepad

Page 11: Build Your Own Hlsls Hader Effect Lab

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:

sampler2D Input : 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:

sampler2D Input : register(s0);

float4 main(float2 uv : 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

Page 12: Build Your Own Hlsls Hader Effect Lab

will calculate the new value of a pixel’s x coordinate using the pixel’s y component as an offset and vice-versa for the pixel’s 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:

sampler2D Input : register(s0);

float4 main(float2 uv : 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:

sampler2D Input : register(s0);

float4 main(float2 uv : TEXCOORD) : COLOR{ float4 Color; 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); return Color;}

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 editora. Start Run

Page 13: Build Your Own Hlsls Hader Effect Lab

b. In the “Open:” text field type cmd, and select OK:

9. Navigate to the location where you saved the .fx HLSL file previously10. 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, let’s 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.

Page 14: Build Your Own Hlsls Hader Effect Lab

16. Now let’s 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…

Page 15: Build Your Own Hlsls Hader Effect Lab

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.

Page 16: Build Your Own Hlsls Hader Effect Lab

19. Let’s 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 Schechter’s 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.

Page 17: Build Your Own Hlsls Hader Effect Lab

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

Page 18: Build Your Own Hlsls Hader Effect Lab

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);

Page 19: Build Your Own Hlsls Hader Effect Lab

}

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 let’s add a C# wrapper for our HLSL program26. 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 Schechter’s 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;

Page 20: Build Your Own Hlsls Hader Effect Lab

// Update each DependencyProperty that's registered with a shader register. This // is needed to ensure the shader gets sent the proper default value. UpdateShaderValue(InputProperty); }

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:

Page 21: Build Your Own Hlsls Hader Effect Lab

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)

Page 22: Build Your Own Hlsls Hader Effect Lab

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:

Page 23: Build Your Own Hlsls Hader Effect Lab

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:

Page 24: Build Your Own Hlsls Hader Effect Lab

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 Studio2. Go to File New Project and under NET Framework 3.5 choose WPF Application.3. Enter HLSLEffectLab as the Name of your project.

Page 25: Build Your Own Hlsls Hader Effect Lab

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. Let’s 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

Page 26: Build Your Own Hlsls Hader Effect Lab

4. Name the Folder Images5. Right-click on the Images folder.

a. Add Existing Item…

Page 27: Build Your Own Hlsls Hader Effect Lab

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 let’s 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>

Page 28: Build Your Own Hlsls Hader Effect Lab

<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.

Page 29: Build Your Own Hlsls Hader Effect Lab

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.

Page 30: Build Your Own Hlsls Hader Effect Lab

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 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;

namespace HLSLEffectLab

Page 31: Build Your Own Hlsls Hader Effect Lab

{ /// <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

Page 32: Build Your Own Hlsls Hader Effect Lab

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 solution’s 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.

Page 33: Build Your Own Hlsls Hader Effect Lab

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 System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Windows;using System.Windows.Controls;using System.Windows.Data;

Page 34: Build Your Own Hlsls Hader Effect Lab

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;using 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:

Page 35: Build Your Own Hlsls Hader Effect Lab

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> <Image Name="MainImage" Stretch="Fill" Grid.Row="0" > <Image.Effect> <eff:WaveEffect x:Name="waveEff"/> </Image.Effect> </Image><Grid Grid.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:

...using System.Windows.Input;using System.Windows.Media;using System.Windows.Media.Effects;

Page 36: Build Your Own Hlsls Hader Effect Lab

using System.Windows.Media.Imaging;using System.Windows.Media.Animation;using System.Windows.Navigation;...

3. Next we will add a method to perform the animation. Add the following method to Window1.xaml.cs:

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); }

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; let’s 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:

...sampler2D Input : register(s0);float frequency : register(C0);

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;}...

... public WaveEffect()

Page 37: Build Your Own Hlsls Hader Effect Lab

{this.PixelShader = _pixelShader;

// Update each DependencyProperty that's registered with a shader register. This// is needed to ensure the shader gets sent the proper default value.UpdateShaderValue(InputProperty);UpdateShaderValue(WavinessProperty);}.........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)));...

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:

... private void Image_MouseEnter(object sender, MouseEventArgs e) { MainImage.Source = ((Image)sender).Source; MainImage.Effect = new ShaderEffectLibrary.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"

Page 38: Build Your Own Hlsls Hader Effect Lab

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 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;

Page 39: Build Your Own Hlsls Hader Effect Lab

using System.Windows.Media.Effects;using System.Windows.Media.Imaging;using System.Windows.Media.Animation;using System.Windows.Navigation;using System.Windows.Shapes;using 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.)//-----------------------------------------------------------------------------------------

Page 40: Build Your Own Hlsls Hader Effect Lab

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 System;using System.Windows;using System.Windows.Media;using 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. This // is needed to ensure the shader gets sent the proper default value. UpdateShaderValue(InputProperty); UpdateShaderValue(WavinessProperty); }

#endregion

Page 41: Build Your Own Hlsls Hader Effect Lab

#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) {

Page 42: Build Your Own Hlsls Hader Effect Lab

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; }}