Whilst my day job continues on its ever-accelerating decline 🙂 I thought I’d entertain myself by experimenting with multi-input pixel shaders in WPF following on from the post I wrote over here.
Now, luckily for me Greg over here has already talked about it and it turns out to be not much of a stretch over what I did previously – you define a 2nd input for your shader of type brush and then you can sample from either input in order to produce the resultant effect.
I took my previously created “Swirl” effect and added a second brush to make a sort of “BlendAndSwirl” effect;
float factor : register(C0);
float blend : register(C1);sampler2D input : register(S0);
sampler2D input2 : register(S1);float4 main(float2 uv : TEXCOORD) : COLOR
{
float angle = -3.14;
float yDist = uv.y – 0.5;
float xDist = uv.x – 0.5;
float dist = sqrt(xDist * xDist + yDist * yDist);
angle *= dist * factor;float xN = cos(angle) * xDist – sin(angle) * yDist;
float yN = sin(angle) * xDist + cos(angle) * yDist;
uv.x = xN + 0.5;
uv.y = yN + 0.5;
float4 img1Colour = tex2D(input, uv);
float4 img2Colour = tex2D(input2, uv);
img1Colour *= blend;
img2Colour *= (1.0 – blend);
img2Colour += img1Colour;
return(img2Colour);
}
and then wrote the C# to go hand in hand with it;
public class BlendSwirlEffect : ShaderEffect { static BlendSwirlEffect() { _pixelShader.UriSource = Global.MakePackUri("effect1.ps"); } public BlendSwirlEffect() { this.PixelShader = _pixelShader; UpdateShaderValue(InputProperty); UpdateShaderValue(Input2Property); UpdateShaderValue(SwirlProperty); } public Brush Input { get { return (Brush)GetValue(InputProperty); } set { SetValue(InputProperty, value); } } public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(BlendSwirlEffect), 0); public Brush Input2 { get { return ((Brush)GetValue(Input2Property)); } set { SetValue(Input2Property, value); } } public static readonly DependencyProperty Input2Property = ShaderEffect.RegisterPixelShaderSamplerProperty("Input2", typeof(BlendSwirlEffect), 1); public double Swirl { get { return (double)GetValue(SwirlProperty); } set { SetValue(SwirlProperty, value); } } public static readonly DependencyProperty SwirlProperty = DependencyProperty.Register("Swirl", typeof(double), typeof(BlendSwirlEffect), new UIPropertyMetadata(0.0, PixelShaderConstantCallback(0))); public double Blend { get { return (double)GetValue(BlendProperty); } set { SetValue(BlendProperty, value); } } public static readonly DependencyProperty BlendProperty = DependencyProperty.Register("Blend", typeof(double), typeof(BlendSwirlEffect), new UIPropertyMetadata(0.0, PixelShaderConstantCallback(1))); private static PixelShader _pixelShader = new PixelShader(); }
there’s nothing too magical going on in here – we just have a 2nd brush to play with and sample and then I’ve got 2 factors to control the “level” of Blending of one picture with another and the “factor” applied to the Swirl.
I then built a little UI to use the effect;
<Grid x:Name="mainGrid"> <Grid.Resources> <ImageBrush x:Key="brush1" ImageSource="c:\windows\web\wallpaper\img1.jpg" Stretch="Fill" /> <Storyboard x:Key="sbAnimate" Storyboard.TargetName="blendEffect" FillBehavior="HoldEnd"> <DoubleAnimation Storyboard.TargetProperty="Blend" From="0" To="1" BeginTime="00:00:00.5" Duration="00:00:00.5"/> <DoubleAnimation Storyboard.TargetProperty="Swirl" From="0" To="4" Duration="00:00:01"/> <DoubleAnimation Storyboard.TargetProperty="Swirl" From="4" To="0" Duration="00:00:01" BeginTime="00:00:02"/> </Storyboard> </Grid.Resources> <Grid.RowDefinitions> <RowDefinition Height="4*" /> <RowDefinition /> </Grid.RowDefinitions> <Image Source="c:\windows\web\wallpaper\img2.jpg" Stretch="Fill" Margin="10"> <Image.Effect> <local:BlendSwirlEffect x:Name="blendEffect" Input2="{DynamicResource brush1}" Swirl="{Binding ElementName=swirlSlider,Path=Value}" Blend="{Binding ElementName=blendSlider,Path=Value}" /> </Image.Effect> </Image> <StackPanel Grid.Row="1"> <Grid HorizontalAlignment="Stretch"> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <StackPanel Margin="10"> <Slider x:Name="blendSlider" Minimum="0" Maximum="1" MinWidth="192" Margin="5"/> <TextBlock FontSize="14" Text="Blend" TextAlignment="Center" /> </StackPanel> <StackPanel Margin="10" Grid.Column="1"> <Slider x:Name="swirlSlider" Minimum="0" Maximum="4" MinWidth="192" Margin="5"/> <TextBlock FontSize="14" Text="Swirl" TextAlignment="Center" /> </StackPanel> </Grid> <Button FontSize="14" Content="Animate" Click="OnAnimate" MinHeight="48" MinWidth="192" HorizontalAlignment="Center"/> </StackPanel> </Grid>
Which just defines an Image that’s using the BlendSwirlEffect and a second image brush that it’s using as the 2nd image to sample in the pixel shader ( Input2 in the code and the XAML ).
This gives a UI like thi;s
where I can then go and blend/swirl a little;
and I stuck a little animation on there that animates both properties at the same time. Here’s the project code if you want to play with it – note that to compile it you’ll need the directX SDK so I’ve included the binary files in the project in case you just want to run it.