Pixel Shader Based Panning
Best animation performance in Silverlight 4 is obtained from the combination of procedural animation and a pixel shader, as reported in a previous blog. I know, a pixel shader is not really meant to be used for spatial manipulation. However, in Silverlight 4 vertex and geometry shaders are not available. Also, pixel shaders are limited to Model 2 shaders, and only limited data exchange is possible. The question is then, “how far can we push pixel shaders model 2”. Another previous blog post discussed a preliminary version of dispersion. This post is about Panning. Panning means here that the size and coordinates of a sub frame of, in this case a video, are changed.
The Pixel Shader
The pixel shader was created using Shazzam. The first step is to reduce the image within its drawing surface, thus creating a frame that works as a window through which parts of the video are visible. Panning is in fact just changing the coordinates of this window while correcting for the change in coordinates when sampling the source texture.
In the pixel shader below, the BlockSize in [0, 1], BlockX, and BlockY, both in [0, 1] define the panning window. The Bound function centers the panning window for coordinate values of 0.5. In the Main function, the ‘space’ variable denotes the available space to move the panning window around in. BlockH and BlockV denote the topmost horizontal and leftmost vertical edges of the panning window. We use these to filter input coordinates that sample the color texture. Other texels get assigned the background color of the demo application.
sampler2D input : register(s0); // new HLSL shader /// <summary>Size of BLock</summary> /// <minValue>0.0</minValue> /// <maxValue>1.0</maxValue> /// <defaultValue>1.0</defaultValue> float BlockSize : register(C2); /// <summary>Horizontal Block selection</summary> /// <minValue>0.0</minValue> /// <maxValue>1.0</maxValue> /// <defaultValue>0.5</defaultValue> float BlockX : register (C3); /// <summary>Vertical Block selection</summary> /// <minValue>0.0</minValue> /// <maxValue>1.0</maxValue> /// <defaultValue>0.5</defaultValue> float BlockY : register (C4); float2 Bounds(float coord, float space) { // Scale coordinate to available space float blockStart = coord * space; return float2(blockStart, blockStart + BlockSize); // 2nd argument is block end } float4 main(float2 uv : TEXCOORD) : COLOR { /* Helpers */ // Background Color float4 Background = {0.937f, 0.937f, 0.937f, 1.0f}; // Available space to move around float space = 1.0f - BlockSize; /* Define Block */ float2 BlockH = Bounds(BlockX, space); float2 BlockV = Bounds(BlockY, space); // If uv in BLock, sample if (uv.x >= BlockH.x && uv.x <= BlockH.y && uv.y >= BlockV.x && uv.y <= BlockV.y) return tex2D(input, uv); else return Background; }
Client Application
The above shader is used in an App that runs a video fragment, and can be explored at my App Shop. The application has controls for panning window size, X-coordinate and Y-coordinate of the panning window on the video surface. The video used in the application is a fragment of “Big Buck Bunny”, an open source animated video, made using only open source software tools.
Animation
Each of the above controls can be animated independently. Animation is implemented using Storyboards for the slider control values. Hence you‘ll see them slide during animation. The App is configured to take advantage of GPU acceleration, where possible. It really needs that for smooth animation. Also the maximum frame rate has been raised to 1000.
Performance Statistics
The animations run at about 270 FPS on my pc. This requires both significant effort from both the GPU as from the CPU – both about 35% of the processor time. The required memory approaches 2.2Mb.