Pixel Shaders w/ Source (And a demo!)

In an earlier post, I showed some still images of Silverlight pixel shaders and how we used the awesome tool Shazzam in our development.  Today I’d like to post the code and show some interesting uses of the shaders.  (Live!)

Let’s start with the code for the Telescopic Blur effect.  Here’s the relevant HLSL:

/// <summary>Center X of the Zoom.</summary>
/// <minValue>0</minValue>
/// <maxValue>1</maxValue>
/// <defaultValue>0.5</defaultValue>
float CenterX : register(C0);

/// <summary>Center Y of the Zoom.</summary>
/// <minValue>0</minValue>
/// <maxValue>1</maxValue>
/// <defaultValue>0.5</defaultValue>
float CenterY : register(C1);

/// <summary>Amount of zoom blur.</summary>
/// <minValue>0</minValue>
/// <maxValue>3</maxValue>
/// <defaultValue>2.5</defaultValue>
float BlurAmount : register(C2);

sampler2D input : register(s0);

float4 main(float2 uv : TEXCOORD) : COLOR

{
    float4 c = 0;
    uv.x -= CenterX;
    uv.y -= CenterY;

    float distanceFactor = pow(pow(uv.x,2) + pow(uv.y, 2),2);

    for(int i=0; i < 15; i++)
    {
        float scale = 1.0 - distanceFactor * BlurAmount * (i / 30.0);
        float2 coord = uv * scale;
        coord.x += CenterX;
        coord.y += CenterY;
        c += tex2D(input,coord);
    }

    c /= 15;
    return c;
}

It takes 3 inputs.  By altering the center of the zoom, you can do some fun stretching motions.  As you’ll see in the demo below, it turned out to be a neat way to make text fly into view.  You can also alter the amount of zoom blur.  I’ll leave it up to the reader to play with this within Shazzam.

Next we have the underwater effect:

sampler2D input : register(s0);

float Timer : register(C0);

static const float2 poisson[12] =
{
        float2(-0.326212f, -0.40581f),
        float2(-0.840144f, -0.07358f),
        float2(-0.695914f, 0.457137f),
        float2(-0.203345f, 0.620716f),
        float2(0.96234f, -0.194983f),
        float2(0.473434f, -0.480026f),
        float2(0.519456f, 0.767022f),
        float2(0.185461f, -0.893124f),
        float2(0.507431f, 0.064425f),
        float2(0.89642f, 0.412458f),
        float2(-0.32194f, -0.932615f),
        float2(-0.791559f, -0.59771f)
};

float4 main(float2 uv : TEXCOORD) : COLOR
{
	float2 Delta = { sin(Timer + uv.x*23 + uv.y*uv.y*17)*0.02 , cos(Timer + uv.y*32 + uv.x*uv.x*13)*0.02 };

    float2 NewUV = uv + Delta;

	float4 Color = 0;
	for (int i = 0; i < 12; i++)
	{
	   float2 Coord = NewUV + (poisson[i] / 50);
       Color += tex2D(input, Coord)/12.0;
     }
     Color += tex2D(input, uv)/4;
     Color.a = 1.0;
     return Color;
}

Its input is a timer value, so that it can be animated.  If you compile this into a Silverlight effect, you can make the timer be a DependencyProperty, which allows the effect to be animated by Storyboard.  I love XAML.

While this is a “passable” underwater effect (and I’m still working on a better one), I found an unanticipated use for it.  But first, a digression.

A year or two ago, LucasArts re-released their classic game The Secret of Monkey Island for the XBox.  I played this game extensively as a teenager, so of course I downloaded the new version and played through again, to see what they had done with the new capabilities of modern hardware.  I wasn’t disappointed.  While upgrading the look, they had stayed faithful to the design spirit of the original.  And one of the most impressive effects was the Ghost Pirate LeChuck.  He had a wavy, wispy quality.  (As shown here.)  It was fluid and animated and very impressive, and at the time I didn’t know how they did it.

Fast forward a year or two and we’re working on the underwater shader.  On a whim, I applied it to a white square in front of a black background.  Whoah.  Now I knew.  So we put together a quick ghost demo using the underwater effect and you can see it, along with the telescopic effect used as a transition, by clicking here.

Click for Pixel Shaders Demo

Click for Pixel Shaders Demo

  1. No trackbacks yet.

Leave a comment