Introduction
It is easy to create pixel shaders in Silverlight. It's basically C# like code. Language for creating shaders is called HLSL - High Level Shader Language. HLSL is heavily used inside video games to create cool graphics. :) At the moment Silverlight doesn't allow you to define vertex shaders, you can do only pixel.
Please read my blog: Cellbi Software Blog.
See how reflection shader works: Silverlight Reflection Shader Effect.
Steps to Create New Pixel Shader
- Create new text file with "fx" extension and place your HLSL code there.
- Then compile (not with Visual Studio, but with special effect compiler tool available from DirectX SDK - you can download this from Microsoft web site) your "fx" file to get compiled pixel shader. Compiled pixel shader is placed inside binary file with "ps" extension.
- Make sure to include this "ps" file into your project and set its compile type to "Resource".
- And finally create new "*.cs" file and place C# shader code there.
Fx File
Effect file should contain HLSL instructions in order to compile. If you are new to HLSL, then read HLSL reference here: HLSL Reference should give you information on language syntax, available data types, operators and functions.
Here is how the simplest "fx" file looks like:
sampler2D input : register(s0);
float4 main(float2 uv : TEXCOORD) : COLOR
{
return tex2D(input, uv);
}
This line: "sampler2D input : register(s0);
" defines "input" variable of sampler2D
type. This instruction "register(s0)
" defines that data for input variable comes from "s0
" register. Next main function is defined - it's C like syntax, except for "TEXTCOORD
" and "COLOR
" - these are HLSL semantics used by effect compiler and GPU. TEXTCOORD
means that "uv
" parameter contains texture coordinates, which can get values from 0 to 1. Since "uv
" variable has float2
data type, it's possible to access it's "u
" and "v
" components, e.g. "uv.u
", "uv.v
", or you can use "uv.x
", "uv.y
". COLOR
semantic means that main function returns color data. "text2D
" used inside main function's body is HLSL function which returns color data from the given sampler and texture coordinates.
We are going to define Reflect
function for our reflection shader. See below:
float4 Reflect(float2 uv : TEXCOORD) : COLOR
{
float edge = 0.5;
if (uv.y > edge)
{
uv.y = edge - (uv.y - edge);
return tex2D(input, uv) * uv.y;
}
return tex2D(input, uv);
}
The code is really simple, if uv.y > 0.5
, we reflect uv
coordinate and return color
data, otherwise we use original texture coordinate to return color
data. So now we just call Reflect
function from our main
:
float4 main(float2 uv : TEXCOORD) : COLOR
{
return Reflect(uv);
}
To compile fx
code, you need to run the following command line:
fxc /T ps_2_0 /E main /Fo "Reflection.ps" "Reflection.fx"
For more information, see Fxc Tool.
You can define pre build action for your project:
"fxc" /T ps_2_0 /E main /Fo "$(ProjectDir)Reflection.ps" "$(ProjectDir)Reflection.fx"
fxc
tool will create "Reflection.ps" binary file - include it in your project and set compile type to resource.
C# Shader File
Create a new C# file, and paste the following code:
using System;
using System.Windows.Media.Effects;
using System.Windows;
namespace ReflectionShader
{
public class ReflectionShader : ShaderEffect
{
public ReflectionShader()
{
Uri u = new Uri(@"ReflectionShader;component/Reflection.ps", UriKind.Relative);
PixelShader = new PixelShader() { UriSource = u };
}
public static readonly DependencyProperty ElementHeightProperty =
DependencyProperty.Register("ElementHeight",
typeof(double), typeof(ReflectionShader),
new PropertyMetadata(100.0, OnElementHeightChanged));
static void OnElementHeightChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
(d as ReflectionShader).OnElementHeightChanged((double)e.OldValue,
(double)e.NewValue);
}
protected virtual void OnElementHeightChanged(double oldValue, double newValue)
{
PaddingBottom = newValue;
}
public double ElementHeight
{
get { return (double)base.GetValue(ElementHeightProperty); }
set { base.SetValue(ElementHeightProperty, value); }
}
}
}
Take a look at the code inside the ReflectionShader
constructor - it sets PixelShader
property inherited from ShaderEffect
class. "ReflectionShader;component/Reflection.ps" - this string
sets relative URL to our "ps" file. Since reflection shader needs some space below UI element, we use PaddingBottom
property inside OnElementHeightChanged
method.
Hot to Use Pixel Shader
It's really simple. Here is the XAML code:
<Button x:Name="btnReflected" Width="200" Height="70">
<TextBlock FontWeight="Bold" FontSize="25" Text="Reflection" />
<Button.Effect>
<local:ReflectionShader ElementHeight="70">
</local:ReflectionShader>
</Button.Effect>
</Button>
That's all.
History
- 25th May, 2009: Initial post