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:

noise

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:

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:

noise64x64

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

Download the effect, sources and dll, here.