Adding Noise Using a WPF Shader Effect
Inspired by this post I decided to try and create a noise effect to make it easier to add some noise to the, sometimes too smooth, gradients in Windows Presentation Foundation.I wanted the XAML code to be as simple as possible:
<Rectangle Fill="{StaticResource gradient}">
<Rectangle.Effect>
<eff:NoiseEffect Ratio="0.2" />
</Rectangle.Effect>
</Rectangle>
Here is how I accomplished this.
I started with Shazzam and created a pixel shader:
sampler2D input : register(s0);
sampler2D randomInput : register(s1);
/// <summary>Ratio: 0 is 100% input source, 1 is full noise</summary>
/// <minValue>0/minValue>
/// <maxValue>1</maxValue>
/// <defaultValue>.5</defaultValue>
float Ratio : register(C0);
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 inputTex = tex2D(input, uv);
float4 randomTex = tex2D(randomInput, uv);
float4 noisedTex = inputTex + (inputTex * randomTex*Ratio);
return noisedTex;
}
The pixel shader takes the current pixel and highlights the color of the pixel with a sample from a second image. Of course the second image can be anything so this shader can also be used to create a watermark effect.
I extended constructor in the ShaderEffect class that was generated by Shazzam a little:
public NoiseEffect()
{
PixelShader pixelShader = new PixelShader();
pixelShader.UriSource =
new Uri("/Shaders;component/Noise.ps", UriKind.Relative);
this.PixelShader = pixelShader;
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.UriSource =
new Uri("pack://application:,,,/Shaders;component/Images/noise.png");
bitmap.EndInit();
this.RandomInput =
new ImageBrush(bitmap)
{
TileMode = System.Windows.Media.TileMode.Tile,
Viewport = new Rect(0, 0, 800, 600),
ViewportUnits = BrushMappingMode.Absolute
};
this.UpdateShaderValue(InputProperty);
this.UpdateShaderValue(RandomInputProperty);
this.UpdateShaderValue(RatioProperty);
}
I added the loading of a default bitmap (noise.png) that is embedded in the Shaders.dll. The bitmap is 800 by 600 pixels and to prevent scaling I set the TileMode to Tile and adjusted the Viewport so the image will be repeated instead of being stretched.
Here’s the bitmap, created with Paint.NET:
All that was left was creating a demo that shows the difference between not using the effect, the default effect and the effect using a custom image:
The top image shows the linear gradient without any effects. The second with the default noise and the last one with a custom circular image:
The third rectangle is much brighter; the higher the effect ratio the lighter the image will become. The ratio of the middle rectangle is 0.2 and the last rectangle has an effect ratio of 1.0