Saturday, February 04, 2012
Download

CustomShaderEffect.zip

Before we start creating our own Shader Effects for Silverlight we would need following tools:

DirectX SDK

Shazzam (Optional BUT very useful)

Silverlight 3

.Net 3.5 SP1

VS 2008

Bassically we start out with .fx file and compiles into .ps which then gets loaded into Silverlight 3 as ShadderEffect and can be used in Blend. Fx file is HLSL is the High Level Shading Language for DirectX.

Here are some nice blogs talking about this topic

http://www.wintellect.com/CS/blogs/jprosise/archive/2009/03/25/silverlight-3-s-new-pixel-shaders.aspx

http://khason.net/blog/brightness-and-contrast-manipulation-in-wpf-35-sp1/

http://blog.wpfwonderland.com/2008/10/08/shazzam-wpf-pixel-shader-effect-testing-tool-now-available/

CustomShaderEffect.zip

Before we start creating our own Shader Effects for Silverlight we would need following tools:

DirectX SDK

Shazzam (Optional BUT very useful)

Silverlight 3

.Net 3.5 SP1

VS 2008

Bassically we start out with .fx file and compiles into .ps which then gets loaded into Silverlight 3 as ShadderEffect and can be used in Blend. Fx file is HLSL is the High Level Shading Language for DirectX.

Here are some nice blogs talking about this topic

http://www.wintellect.com/CS/blogs/jprosise/archive/2009/03/25/silverlight-3-s-new-pixel-shaders.aspx

http://khason.net/blog/brightness-and-contrast-manipulation-in-wpf-35-sp1/

http://blog.wpfwonderland.com/2008/10/08/shazzam-wpf-pixel-shader-effect-testing-tool-now-available/



Step By Step

CustomShaderEffect.csproj

Create Ripple.fx file

Ripple.fx was copied from opensource WPF shader effect source from my previous blog.

 

float progress : register(C0);

sampler2D implicitInput : register(s0);

sampler2D oldInput : register(s1);

 

struct VS_OUTPUT

{

    float4 Position  : POSITION;

    float4 Color    : COLOR0;

    float2 UV        : TEXCOORD0;

};

 

float4 Ripple(float2 uv)

{

    float frequency = 20;

    float speed = 10;

    float amplitude = 0.05;

    float2 center = float2(0.5,0.5);

    float2 toUV = uv - center;

    float distanceFromCenter = length(toUV);

    float2 normToUV = toUV / distanceFromCenter;

 

    float wave = cos(frequency * distanceFromCenter - speed * progress);

    float offset1 = progress * wave * amplitude;

    float offset2 = (1.0 - progress) * wave * amplitude;

 

    float2 newUV1 = center + normToUV * (distanceFromCenter + offset1);

    float2 newUV2 = center + normToUV * (distanceFromCenter + offset2);

 

    float4 c1 = tex2D(oldInput, newUV1);

    float4 c2 = tex2D(implicitInput, newUV2);

 

    return lerp(c1, c2, progress);

}

 

float4 main(VS_OUTPUT input) : COLOR0

{

    return Ripple(input.UV);

}

 

Compile Ripple.fx shader to Ripple.ps

 

C:\Program Files (x86)\Microsoft DirectX SDK (March 2009)\Utilities\bin\x64\fxc /T ps_2_0 /Fo Ripple.ps Ripple.fx

 

OR

 

Use Shazzam http://shazzam-tool.com/

 

Remember that if you are using Shazzam it will also give you c#Shader Effect file. MAKE sure to use PropertyMetadata instead of UIPropertyMetadata because it will not compile in Silverlight.

 

RippleEffect.cs

 

namespace CustomShaderEffect

{

    public class RippleEffect : ShaderEffect

    {

 

        public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(RippleEffect), 0);

 

        public static readonly DependencyProperty ProgressProperty = DependencyProperty.Register("Progress", typeof(double), typeof(RippleEffect), new System.Windows.PropertyMetadata(new double(), PixelShaderConstantCallback(0)));

 

        private static PixelShader pixelShader;

 

        static RippleEffect()

        {

            pixelShader = new PixelShader();

            Uri u = new Uri(@"/CustomShaderEffect;component/CustomShaderEffect/Ripple.ps", UriKind.Relative);

            pixelShader.UriSource = u;

        }

 

        public RippleEffect()

        {

            this.PixelShader = pixelShader;

            this.UpdateShaderValue(InputProperty);

            this.UpdateShaderValue(ProgressProperty);

        }

 

        [System.ComponentModel.BrowsableAttribute(false)]

        public Brush Input

        {

            get

            {

                return ((System.Windows.Media.Brush)(GetValue(InputProperty)));

            }

            set

            {

                SetValue(InputProperty, value);

            }

        }

 

        public double Progress

        {

            get

            {

                return ((double)(GetValue(ProgressProperty)));

            }

            set

            {

                SetValue(ProgressProperty, value);

            }

        }

    }

}

 

Notice here that [System.ComponentModel.BrowsableAttribute(false)] does not exist in Silverlight and to get around this problem you have to have

namespace System.ComponentModel

{

    public class BrowsableAttribute : Attribute

    {

 

        public BrowsableAttribute(bool unused)

        {

        }

    }

}

This is necessary because you want to avoid Brush input from being able to be choosen from Blend UI and not get error !!!

mainpage.xaml 

<UserControl

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

   xmlns:local="clr-namespace:CustomShaderEffect"

   mc:Ignorable="d" x:Class="CustomShaderEffect.MainPage"

   d:DesignWidth="640" d:DesignHeight="300">

    <UserControl.Resources>

        <Storyboard x:Name="Storyboard1">

            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ucRobot" Storyboard.TargetProperty="(UIElement.Effect).(RippleEffect.Progress)">

                <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>

                <EasingDoubleKeyFrame KeyTime="00:00:03" Value="1"/>

            </DoubleAnimationUsingKeyFrames>

        </Storyboard>

    </UserControl.Resources>

  <Grid x:Name="LayoutRoot" Width="640" Height="300">

        <local:Robot x:Name="ucRobot" Width="200" Height="200" Margin="288,38,152,62" >

            <local:Robot.Effect>

                <local:RippleEffect Input="{x:Null}"/>

            </local:Robot.Effect>

        </local:Robot>

        <Button HorizontalAlignment="Left" Margin="50,78,0,122" Width="196" Content="Show me Ripple Effect" Click="Button_Click" Height="100"/>

    </Grid>

</UserControl>

From Blend we apply out Effect. Please refer to my previous blog on how to do this!

CustomShaderEffect.csproj

Create Ripple.fx file

Ripple.fx was copied from opensource WPF shader effect source from my previous blog.

 

float progress : register(C0);

sampler2D implicitInput : register(s0);

sampler2D oldInput : register(s1);

 

struct VS_OUTPUT

{

    float4 Position  : POSITION;

    float4 Color    : COLOR0;

    float2 UV        : TEXCOORD0;

};

 

float4 Ripple(float2 uv)

{

    float frequency = 20;

    float speed = 10;

    float amplitude = 0.05;

    float2 center = float2(0.5,0.5);

    float2 toUV = uv - center;

    float distanceFromCenter = length(toUV);

    float2 normToUV = toUV / distanceFromCenter;

 

    float wave = cos(frequency * distanceFromCenter - speed * progress);

    float offset1 = progress * wave * amplitude;

    float offset2 = (1.0 - progress) * wave * amplitude;

 

    float2 newUV1 = center + normToUV * (distanceFromCenter + offset1);

    float2 newUV2 = center + normToUV * (distanceFromCenter + offset2);

 

    float4 c1 = tex2D(oldInput, newUV1);

    float4 c2 = tex2D(implicitInput, newUV2);

 

    return lerp(c1, c2, progress);

}

 

float4 main(VS_OUTPUT input) : COLOR0

{

    return Ripple(input.UV);

}

 

Compile Ripple.fx shader to Ripple.ps

 

C:\Program Files (x86)\Microsoft DirectX SDK (March 2009)\Utilities\bin\x64\fxc /T ps_2_0 /Fo Ripple.ps Ripple.fx

 

OR

 

Use Shazzam http://shazzam-tool.com/

 

Remember that if you are using Shazzam it will also give you c#Shader Effect file. MAKE sure to use PropertyMetadata instead of UIPropertyMetadata because it will not compile in Silverlight.

 

RippleEffect.cs

 

namespace CustomShaderEffect

{

    public class RippleEffect : ShaderEffect

    {

 

        public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(RippleEffect), 0);

 

        public static readonly DependencyProperty ProgressProperty = DependencyProperty.Register("Progress", typeof(double), typeof(RippleEffect), new System.Windows.PropertyMetadata(new double(), PixelShaderConstantCallback(0)));

 

        private static PixelShader pixelShader;

 

        static RippleEffect()

        {

            pixelShader = new PixelShader();

            Uri u = new Uri(@"/CustomShaderEffect;component/CustomShaderEffect/Ripple.ps", UriKind.Relative);

            pixelShader.UriSource = u;

        }

 

        public RippleEffect()

        {

            this.PixelShader = pixelShader;

            this.UpdateShaderValue(InputProperty);

            this.UpdateShaderValue(ProgressProperty);

        }

 

        [System.ComponentModel.BrowsableAttribute(false)]

        public Brush Input

        {

            get

            {

                return ((System.Windows.Media.Brush)(GetValue(InputProperty)));

            }

            set

            {

                SetValue(InputProperty, value);

            }

        }

 

        public double Progress

        {

            get

            {

                return ((double)(GetValue(ProgressProperty)));

            }

            set

            {

                SetValue(ProgressProperty, value);

            }

        }

    }

}

 

Notice here that [System.ComponentModel.BrowsableAttribute(false)] does not exist in Silverlight and to get around this problem you have to have

namespace System.ComponentModel

{

    public class BrowsableAttribute : Attribute

    {

 

        public BrowsableAttribute(bool unused)

        {

        }

    }

}

This is necessary because you want to avoid Brush input from being able to be choosen from Blend UI and not get error !!!

mainpage.xaml 

<UserControl

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

   xmlns:local="clr-namespace:CustomShaderEffect"

   mc:Ignorable="d" x:Class="CustomShaderEffect.MainPage"

   d:DesignWidth="640" d:DesignHeight="300">

    <UserControl.Resources>

        <Storyboard x:Name="Storyboard1">

            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ucRobot" Storyboard.TargetProperty="(UIElement.Effect).(RippleEffect.Progress)">

                <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>

                <EasingDoubleKeyFrame KeyTime="00:00:03" Value="1"/>

            </DoubleAnimationUsingKeyFrames>

        </Storyboard>

    </UserControl.Resources>

  <Grid x:Name="LayoutRoot" Width="640" Height="300">

        <local:Robot x:Name="ucRobot" Width="200" Height="200" Margin="288,38,152,62" >

            <local:Robot.Effect>

                <local:RippleEffect Input="{x:Null}"/>

            </local:Robot.Effect>

        </local:Robot>

        <Button HorizontalAlignment="Left" Margin="50,78,0,122" Width="196" Content="Show me Ripple Effect" Click="Button_Click" Height="100"/>

    </Grid>

</UserControl>

From Blend we apply out Effect. Please refer to my previous blog on how to do this!



Demo


 

 

Privacy Statement  |  Terms Of Use
Copyright 2009 by New Age Solution Inc.