In Silverlight 3, Shader Effects (aka Pixel Shaders) allow you to modify the rendered pixels of any UI Element before they are composited to the current view. They can be used to add effects to screen elements including shadows, blur, grayscale, redeye removal – pretty much anything you can accomplish by tweaking pixels using an algorithm. Normally, pixel shaders are done using the GPU (video card), but currently in Silverlight 3, Pixel Shaders are rendered using a software-based algorithm. This means that Pixel Shaders in Silverlight aren’t nearly as fast as they might be using the GPU.
Built-in Shader Effects in Silverlight
Silverlight 3 comes with 2 built-in shader effects: Shadow and Blur. To apply these to an element in XAML, we can use property element syntax to specify the effect like so:
<TextBlock Text="THIS TEXT HAS A BLUR EFFECT!" >
<TextBlock.Effect>
<BlurEffect Radius="5" />
</< SPAN>< SPAN>TextBlock.Effect>
< SPAN>< SPAN></TextBlock>
<TextBlock Text="THIS TEXT HAS A SHADOW EFFECT!">
<TextBlock.Effect>
<DropShadowEffect ShadowDepth="4"/>
</< SPAN>< SPAN>TextBlock.Effect>
< SPAN>< SPAN></TextBlock>
Creating New Shader Effects in Silverlight
Shaders are created using High Level Shader Language (HLSL), which was created as part of the DirectX SDK. One great thing about shaders is that they are easy to create, and have been around for awhile because they are used in DirectX and WPF. So you can search around and find lots of cool HLSL samples!
I recommend using a tool to create your HLSL code such as Walt Ritscher’s Shazzam Tool, which allows you to edit and debug HLSL and see output in real time. This is much more convenient than the alternative of using the fxc command line tool provided in the DirectX SDK.
So, here are the steps and tools I use to create a new Pixel Shader effect:
(1) Download and install the DirectX SDK
(2) Install Shazzam (a WPF ClickOnce App)
(3) Run Shazzam. You will likely need to update the Settings for Shazzam, so go ahead and click the Settings Panel to expand it, and make sure the “Location of DirectX FX Compiler” is pointing to the location that you installed the DirectX SDK to in Step (1). You will need to restart Shazzam after changing the directory.
(4) Now you can have some fun with writing a Pixel Shader in HLSL! You can think of a Pixel Shader as a function that takes in every pixel value, and outputs a new value for that pixel. Here is a very simple Pixel Shader example that brightens an element:
sampler2D input : register(s0);
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 Color;
Color = tex2D( input , uv.xy)*4;
return Color;
}
Go ahead and enter the code into the Shazzam editor and hit F5 (or select Tools/Apply Shader). You’ll immediately see the image samples get brighter when they are processed.
(5) Now try a slightly more complex sample. This one processes an image to Grayscale:
sampler2D implicitInput : register(s0);
float factor : register(c0);
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 color = tex2D(implicitInput, uv);
float gray = color.r * 0.3 + color.g * 0.59 + color.b *0.11;
float4 result;
result.r = (color.r - gray) * factor + gray;
result.g = (color.g - gray) * factor + gray;
result.b = (color.b - gray) * factor + gray;
result.a = color.a;
return result;
}
(6) Note that Shazzam also has a bunch of Sample Shaders. Just go to the “Shader Loader” panel and select “Sample Shaders.”
(7) Now let’s see how we can bring these shaders into a Silverlight 3 App. In Shazzam, select Tools/View Compiled Shaders (*.ps). This will open up the directory containing the shader that you just compiled. Copy the .ps file to the clipboard for the next step.
(8) Create a new Silverlight 3 Application.Paste the .ps file into the Silverlight project and set its Build Action to Resource.
(9) Add a new Class file to the Silverlight Project named MyShaderDemo and paste the following code
into the class (be sure to replace temp.ps with your own compiled shader file):
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Effects;
using System;
public class MyShaderDemo : ShaderEffect
{
public MyShaderDemo()
{
Uri u = new Uri(@"/CustomPixelShader;component/temp.ps", UriKind.Relative);
PixelShader psCustom = new PixelShader();
psCustom.UriSource = u;
PixelShader = psCustom;
}
}