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.
Microsoft Azure and Amazon Web Services (AWS) are two of the most popular cloud platforms.…
Cloud management is difficult to do manually, especially if you work with multiple cloud…
Azure’s scalable infrastructure is often cited as one of the primary reasons why it's the…
https://www.youtube.com/watch?v=wDzCN0d8SeA Watch our "Unlocking the Power of AI in your Software Development Life Cycle (SDLC)"…
FinOps is a strategic approach to managing cloud costs. It combines financial management best practices…
Using Kubernetes with Azure combines the power of Kubernetes container orchestration and the cloud capabilities…
View Comments
PingBack from http://isilverlight.cn/jeff-prosises-blog-reflection-shader-for-silverlight-3/
This post was mentioned on Twitter by ittyurl: New at IttyUrl: http://ittyurl.net/lcNL.ashx jeff prosise's blog : reflection shader for silverlight 3
I don't have the DirectX SDK on my pc.
Is it possible to download the WetFloorEffect.ps file ?
Better yet, you can compile FX files into PS files online:
http://silverlightuk.blogspot.com/2009/03/silverlight-web-pixel-shader-compiler.html
It would be a good idea if the url to use the compiler(http://www.voxpeeps.com/slpixelshadercompiler) works.
But it does not work
Thank you for submitting this cool story - Trackback from DotNetShoutout
@malbaladejo
There is an alternative to the online version Jeff mentioned.
Shazzam Pixel Shader Utility.
You can copy Jeff's fx code into Shazzam and it will create the PS file and the C#/VB file for you.
http://shazzam-tool.com
http://blog.shazzam-tool.com
Hi Jeff,
Great post ... this is exactly what I was looking for. Also, thanks for pointing me to Shazaam, that is a very impressive tool.
I was wondering if you could post your code. The problem I see is that the PixelShader HLSL code you posted doesn't match the the C# class that you posted. The C# class has a reflectionDepth and SourceHeight properties while the HLSL code you posted has an RelativeHeight property.
Even if you just could post the HLSL code in a comment that matches the C# code, I wuold be very grateful.
Thanks again!
...Ed
There's no mismatch. The code should compile and run just fine. (It came directly from my VS project.) I expose a dependency property named ReflectionDepth from the shader class and map it to register c0. Then, in the HLSL, I refer to register c0 using the name RelativeHeight. It really doesn't matter whether I use the same name in the C# and the HLSL, although it would arguably improve readability if I did.
BTW, I will publish the whole shebang after (maybe before) TechEd Europe. The projects aren't quite ready for sharing yet because I have some cleanup to do.
Nevermind Jeff, I had created a class with Shazaam using your HLSL code and the properties it generated in the C# were different. When I ran with your class it worked great ... sorry about that.
One question I did have was, what in the HLSL controls the opacity of the reflected image?