Reflection Shader for Silverlight 3

I’ve become enamored with Silverlight behaviors lately because they provide a clean and easy-to-use mechanism for encapsulating complex behavioral logic and applying it to XAML elements. And I’m equally enamored with Silverlight pixel shaders, which allow similar encapsulation of complex visual effects implemented using Microsoft’s High-Level Shader Language, better known as HLSL.

Last spring, I blogged about a technique for creating reflections programmatically using WriteableBitmap. For TechEd Europe week after next, I decided to go one step futher and create a pixel shader that does the same. Called WetFloorEffect, my shader can be applied in XAML the same way built-in shaders such as DropShadowEffect and BlurEffect are applied:

<custom:PenguinUserControl>

  <custom:PenguinUserControl.Effect>

    <custom:WetFloorEffect SourceHeight=”300″ />

  </custom:PenguinUserControl.Effect>

</custom:PenguinUserControl>

SourceHeight is a dependency property that tells the shader how tall the object you’re reflecting is. Generally, you have to play with SourceHeight a little to get the bottom of the object you’re reflecting (in this example, a user control) to line up with the top of the reflection generated by the shader. Here’s the output from this example:

WetFloorEffect also exposes a dependency property named ReflectionDepth, which determines the depth (height) of the reflection relative to the height of the object being reflected. Valid values range from 0.0 (0%) to 1.0 (100%). The following example produces a reflection that is half the height of the object being reflected:

<custom:PenguinUserControl>

  <custom:PenguinUserControl.Effect>

    <custom:WetFloorEffect SourceHeight=”300″ ReflectionDepth=”0.5″ />

  </custom:PenguinUserControl.Effect>

</custom:PenguinUserControl>

And here’s the output:

The shader itself is written in HLSL and looks like this:

sampler2D input : register(s0);

float RelativeHeight : register(c0);

 

float4 main(float2 pos : TEXCOORD) : COLOR

{

    if (pos.y > 0.5)

    {

        pos.y = 0.5 – ((pos.y 0.5) / RelativeHeight);

        return tex2D(input, pos) * pos.y;

    }

    return tex2D(input, pos);

}

Once compiled into a PS file, the shader is encapsulated into a Silverlight effect with the following class definition:

public class WetFloorEffect : ShaderEffect

{

    public static readonly DependencyProperty ReflectionDepthProperty =

        DependencyProperty.Register(“ReflectionDepth”,

        typeof(double), typeof(WetFloorEffect),

        new PropertyMetadata(1.0, PixelShaderConstantCallback(0)));

 

    public double ReflectionDepth

    {

        get { return ((double)(GetValue(ReflectionDepthProperty))); }

        set

        {

            if (value <= 0.0)

                value = 0.00001; // Avoid divide-by-zero errors in HLSL

            if (value > 1.0)

                value = 1.0;

            SetValue(ReflectionDepthProperty, value);

        }

    }

 

    public static readonly DependencyProperty SourceHeightProperty =

        DependencyProperty.Register(“SourceHeight”,

        typeof(double), typeof(WetFloorEffect),

        new PropertyMetadata(0.0, OnSourceHeightChanged));

       

    public double SourceHeight

    {

        get { return (double)GetValue(SourceHeightProperty); }

        set

        {

            if (value < 0.0)

                throw new ArgumentOutOfRangeException

                    (“SourceHeight”, “SourceHeight cannot be negative”);

            SetValue(SourceHeightProperty, value);

        }

    }

 

    static void OnSourceHeightChanged(DependencyObject obj,

        DependencyPropertyChangedEventArgs e)

    {

        ((WetFloorEffect)obj).PaddingBottom = (double)e.NewValue;

    }

       

    public WetFloorEffect()

    {

        PixelShader = new PixelShader() { UriSource =

            new Uri(“/CustomShaderDemo;component/WetFloorEffect.ps”,

            UriKind.Relative) };

        UpdateShaderValue(ReflectionDepthProperty);

        UpdateShaderValue(SourceHeightProperty);

    }

}

There’s a lot going on here, but the gist of it is that WetFloorEffect wraps the compiled HLSL code, which is embedded in the generated assembly as a resource. One item of interest is the call to PixelShaderConstantCallback when the ReflectionDepth dependency property is registered; this maps the value of the property to register c0 in the HLSL, which is in turn mapped to the variable named RelativeHeight. Another point of interest is that the value of SourceHeight is written straight through to the shader’s PaddingBottom property, which is inherited from ShaderEffect. This effectively expands the canvas on which the shader can draw downward by the specified number of pixels.

I’ll explain all this and more during my “Cool Graphics, Hot Code” talk at TechEd. And I’ll have lots of other goodies to share, too.

Jeff Prosise

View Comments

Recent Posts

8-Step AWS to Microsoft Azure Migration Strategy

Microsoft Azure and Amazon Web Services (AWS) are two of the most popular cloud platforms.…

6 days ago

How to Navigate Azure Governance

 Cloud management is difficult to do manually, especially if you work with multiple cloud…

2 weeks ago

Why Azure’s Scalability is Your Key to Business Growth & Efficiency

Azure’s scalable infrastructure is often cited as one of the primary reasons why it's the…

4 weeks ago

Unlocking the Power of AI in your Software Development Life Cycle (SDLC)

https://www.youtube.com/watch?v=wDzCN0d8SeA Watch our "Unlocking the Power of AI in your Software Development Life Cycle (SDLC)"…

1 month ago

The Role of FinOps in Accelerating Business Innovation

FinOps is a strategic approach to managing cloud costs. It combines financial management best practices…

2 months ago

Azure Kubernetes Security Best Practices

Using Kubernetes with Azure combines the power of Kubernetes container orchestration and the cloud capabilities…

2 months ago