More WPF Custom Effects: Motion Blur and Grayscale Samples
Designing visually appealing UI is now easier and more performant with the addition of custom shader-based
effects in WPF for .NET 3.5 Service Pack 1.
This article describes the process of creating a simple Greyscale effect and integrating a motion blur
effect into a ScrollViewer control.
Before we get started, please familiarize yourself with Greg Schechter’s article on
"Writing custom GPU-based Effects for WPF".
It's a good introduction to custom shader effects in WPF.
The steps below describe the general process for implementing a custom shader-based effect for use in WPF applications.
The download links for the Greyscale and Motion Blur effect sample projects are available at the bottom of this post.
-
Implement the pixel shader for your effect using High Level Shader Language (HLSL) and
compile it to generate the pixel shader bytecode that will be loaded in the WPF application.
Details on how to compile the shader can be found in Greg Schechter's article or in the next
section. Please keep in mind that WPF currently supports Pixel Shader 2.0 only.
-
Subclass the WPF ShaderEffect class and create a PixelShader object that will load the
pixel shader file from the previous step. Refer to the GreyEffect.cs or
DirectionalBlurEffect.cs files from the sample projects for the source code
and the explanation of how this is done.
Compiling a Pixel Shader
Install the DirectX SDK to get the tools necessary to compile HLSL (*.fx) source code to *.ps
bytecode that can be loaded using WPF's PixelShader class. Fxc.exe is the HLSL compiler used to
compile *.fx HLSL shader files. The following command shows how to compile Greyscale.fx:
> fxc.exe /T ps_2_0 /E PS /FoGreyscale.ps Greyscale.fx
A few notes on each parameter:
| /T ps_2_0 |
Compiles against the ps_2_0 target pixel shader level that WPF currently supports |
| /E PS |
Marks the function 'PS' as the entry point |
| /FoGreyscale.ps Greyscale.fx |
Takes the file Greyscale.fx as input and output to Greyscale.ps. Note that there is no space between /Fo and Greyscale.ps |
The command above can be copied into a batch file to be used in the pre-build
event of a Visual Studio project to automate the build process. For example:
> call cd "$(ProjectDir)CustomEffects " && "compile.cmd"
A Sample Greyscale Pixel Shader
Effective interaction with the user is an important factor when designing application UI. One
technique is to bring the users' attention to a specific area of the application when an
event occurs. This can be done in different ways, one of which is by using a custom effect.
The screenshots below illustrate the use of a grayscale effect to deemphasize regions and provide
focus on the active portion of the UI where the mouse pointer has moved to.
This is the HLSL used in the above example:
GreyscaleEffectApp\CustomEffects\Source\Greyscale.fx
sampler2D ImageSampler : register(S0); //take ImageSampler from S0 register.
// 'uv' vector from TEXCOORD0 semantics is our
texture coordinate, two floating point numbers in the range 0-1.
float4 PS( float2 uv : TEXCOORD) : COLOR
{
float4 color = tex2D( ImageSampler, uv); // get the color
of texture at the current point
color.rgb = dot(color.rgb, float3(0.3, 0.59, 0.11)); //compose
correct luminance value
return color;
}
A Sample Directional Blur Pixel Shader
It can be useful to provide visual feedback as content quickly changes location such as in a ScrollViewer.
User may have difficulty tracking the scrolling direction if its content is scrolled to by page using the Page
Up or Page Down keys. The scrolling animation will appear less jarring and more natural if a motion blur effect
provides a cue of the direction that the page is being transitioned to. The amount of blur is proportional to
the distance the content is moved. The following screenshots from the sample project illustrates this effect:
This is the pixel shader HLSL used in the directional blur effect:
MotionBlurEffectApp\CustomEffects\Source\DirectionalBlur.fx
float Angle : register(C0); // Defines the blurring direction
float BlurAmount : register(C1); // Defines the blurring magnitude
sampler2D ImageSampler : register(S0); // Defines the image/texture to be blurred
float4 PS( float2 uv : TEXCOORD) : COLOR
{
float4 output = 0; // Defines the output color of a pixel
float2 offset; // Defines the blurring direction as a direction vector
int count = 24; // Defines the number of blurring iterations
//First compute a direction vector which defines the direction of blurring.
This is done using the sincos instruction and the Angle input parameter,
and the result is stored in the offset variable. This vector is of unit
length. Multiply this unit vector by BlurAmount to adjust its length to
reflect the blurring magnitude.
sincos(Angle, offset.y, offset.x);
offset *= BlurAmount;
// To generate the blurred image, we
generate multiple copies of the input image, shifted
according to the blurring direction vector, and then sum
them all up to get the final image.
for(int i=0; i<count; i++)
{
output += tex2D(ImageSampler, uv - offset * i);
}
output /= count; // Normalize the color
return output;
}
The custom MotionScrollViewer in the sample project extends the standard ScrollViewer class and
applies the above effect to its content. When the user scrolls the content of our ScrollViewer,
it computes the direction of movement, amount of blurring, and updates the DirectionalBlur pixel
shader parameters. This is done in the MotionBlurScrollViewer.cs
file in the sample project.
Sample Projects
These projects require .NET Framework 3.5 SP1:
Greyscale Effect: This is the source code (Visual Studio 2008 solution) and
binaries of this sample project can be downloaded from here:
Motion Blur Effect: The source code (Visual Studio 2008 solution) and binaries
of this sample project can be downloaded from here:
Contributors
The following team members for Windows Presentation Foundation at Microsoft contributed to this article:
Meisam Seyed Aliroteh,a Software Design Engineer in Test, Dennis Cheng,
a Lead Software Design Engineer in Test, Tim Cowley, a Software Design Engineer in Test,
Greg Schechter, an Architect, and Linzhen Xuan, a Software Design Engineer in Test.