Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Create Reflection Shader in Silverlight

0.00/5 (No votes)
25 May 2009 1  
Sample code on how to create reflection shader using new pixel shader feature of Silverlight 3

Introduction

It is easy to create pixel shaders in Silverlight. It's basically C# like code. Language for creating shaders is called HLSL - High Level Shader Language. HLSL is heavily used inside video games to create cool graphics. :) At the moment Silverlight doesn't allow you to define vertex shaders, you can do only pixel.

Please read my blog: Cellbi Software Blog.

See how reflection shader works: Silverlight Reflection Shader Effect.

Steps to Create New Pixel Shader

  1. Create new text file with "fx" extension and place your HLSL code there.
  2. Then compile (not with Visual Studio, but with special effect compiler tool available from DirectX SDK - you can download this from Microsoft web site) your "fx" file to get compiled pixel shader. Compiled pixel shader is placed inside binary file with "ps" extension.
  3. Make sure to include this "ps" file into your project and set its compile type to "Resource".
  4. And finally create new "*.cs" file and place C# shader code there.

Fx File

Effect file should contain HLSL instructions in order to compile. If you are new to HLSL, then read HLSL reference here: HLSL Reference should give you information on language syntax, available data types, operators and functions.

Here is how the simplest "fx" file looks like:

sampler2D input : register(s0);

float4 main(float2 uv : TEXCOORD) : COLOR
{
  return tex2D(input, uv);
}

This line: "sampler2D input : register(s0);" defines "input" variable of sampler2D type. This instruction "register(s0)" defines that data for input variable comes from "s0" register. Next main function is defined - it's C like syntax, except for "TEXTCOORD" and "COLOR" - these are HLSL semantics used by effect compiler and GPU. TEXTCOORD means that "uv" parameter contains texture coordinates, which can get values from 0 to 1. Since "uv" variable has float2 data type, it's possible to access it's "u" and "v" components, e.g. "uv.u", "uv.v", or you can use "uv.x", "uv.y". COLOR semantic means that main function returns color data. "text2D" used inside main function's body is HLSL function which returns color data from the given sampler and texture coordinates.

We are going to define Reflect function for our reflection shader. See below:

float4 Reflect(float2 uv : TEXCOORD) : COLOR
{
  float edge = 0.5;
  if (uv.y > edge)
  {
    uv.y = edge - (uv.y - edge);
    return tex2D(input, uv) * uv.y;
  }
  return tex2D(input, uv);
}

The code is really simple, if uv.y > 0.5, we reflect uv coordinate and return color data, otherwise we use original texture coordinate to return color data. So now we just call Reflect function from our main:

float4 main(float2 uv : TEXCOORD) : COLOR
{
  return Reflect(uv);
}

To compile fx code, you need to run the following command line:

fxc /T ps_2_0 /E main /Fo "Reflection.ps" "Reflection.fx"

For more information, see Fxc Tool.

You can define pre build action for your project:

"fxc" /T ps_2_0 /E main /Fo "$(ProjectDir)Reflection.ps" "$(ProjectDir)Reflection.fx"

fxc tool will create "Reflection.ps" binary file - include it in your project and set compile type to resource.

C# Shader File

Create a new C# file, and paste the following code:

using System;
using System.Windows.Media.Effects;
using System.Windows;

namespace ReflectionShader
{
  public class ReflectionShader : ShaderEffect
  {
    public ReflectionShader()
    {
      Uri u = new Uri(@"ReflectionShader;component/Reflection.ps", UriKind.Relative);
      PixelShader = new PixelShader() { UriSource = u };
    }

    public static readonly DependencyProperty ElementHeightProperty =
            DependencyProperty.Register("ElementHeight",
            typeof(double), typeof(ReflectionShader),
            new PropertyMetadata(100.0, OnElementHeightChanged));

    static void OnElementHeightChanged(DependencyObject d, 
      DependencyPropertyChangedEventArgs e)
    {
      (d as ReflectionShader).OnElementHeightChanged((double)e.OldValue, 
						(double)e.NewValue);
    }

    protected virtual void OnElementHeightChanged(double oldValue, double newValue)
    {
      PaddingBottom = newValue;
    }

    public double ElementHeight
    {
      get { return (double)base.GetValue(ElementHeightProperty); }
      set { base.SetValue(ElementHeightProperty, value); }
    }
  }
}

Take a look at the code inside the ReflectionShader constructor - it sets PixelShader property inherited from ShaderEffect class. "ReflectionShader;component/Reflection.ps" - this string sets relative URL to our "ps" file. Since reflection shader needs some space below UI element, we use PaddingBottom property inside OnElementHeightChanged method.

Hot to Use Pixel Shader

It's really simple. Here is the XAML code:

<Button x:Name="btnReflected" Width="200" Height="70">
  <TextBlock FontWeight="Bold" FontSize="25" Text="Reflection" />
  <Button.Effect>
    <local:ReflectionShader ElementHeight="70">
    </local:ReflectionShader>
  </Button.Effect>
</Button>

That's all.

History

  • 25th May, 2009: Initial post

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here