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>