Pixel Shaders in Siverlight 3.0 and WPF !!

Ahora que ha salido Siverlight 3.0 Beta 1, podemos ver que una de las caracteristicas que incluye es soporte para Pixel Shaders, pero realmente, ¿Qué son los Pixel Shaders?

image

Pixel Shader es unas de las partes de un Shader. Shader es un conjunto de instrucciones que la tarjeta gráfica ejecuta, estas instrucciones están muy optimizadas para el trabajo con colores, números de coma flotante y triangulos. Eso significa que los Shaders son como una especie de ensamblador que se ejecuta en la tarjeta gráfica, son muy parecidos a las extensiones multimedia de los procesadores, (MMX SSE).

Como podemos ver en esta grafica la imagen en 3D que se procesa en una tarjeta grafica pasa por diferentes pasos hasta que se muestra en la pantalla, cada uno de estas gráficas se pueden llamar técnicas, que están compuestas de un Verter Shader y de un Pixel Shader.

Verter Shader trabaja con la información de la malla (Mesh) de los objetos 3D que hay en la memoria de la tarjeta grafica a través de matrices.

Una vez que esta información se ha procesado se rellena el objeto con una textura, y ahora se puede procesar un Pixel Shader, que permite aplicar un efecto a cada uno de los pixeles finales de la imagen proyectada del objeto 3D, eso significa que los Pixel Shaders están pensados para imágenes 2D.

Pues bien en todo este conjunto de funcionalidades, en WPF y Silverlight 3.0 Beta1 tenemos soporte para Pixel Shaders.

¿Eso qué significa?, Pues bien, que podemos definir un algoritmo para una imagen 2D que se ejecutará en la tarjeta gráfica.

¿Qué tiene esto de bueno?, Una de las cosas buenas que tiene, es que evidentemente la tarjeta gráfica es mucho más potente en este tipo de cálculos que el procesador, porque está pensado para esto, así que significa que podemos tener efectos espectaculares que ejecutan en tiempo real en WPF y Silverlight 3.0 Beta 1.

Vale, me has convencido, como puedo empezar a escribir uno de estos estupendos shaders?

Vamos a ver como se integran los Pixel Shaders dentro de WPF y de Silverlight 3.0 Beta 1.

Voy a suponer que ya tenemos el Pixel Shader escrito, aquí tenemos uno de ejemplo:

sampler2D input : register(s0);
float factor : register(c0);

float4 main(float2 uv : TEXCOORD) : COLOR
{
    float4 color = 0;
    float angle = -3.14;
    float yDist = uv.y - 0.5;
    float xDist = uv.x - 0.5;
    float dist = sqrt(xDist * xDist + yDist * yDist);
    angle *= dist * factor;
    float xN = cos(angle) * xDist - sin(angle) * yDist;
    float yN = sin(angle) * xDist + cos(angle) * yDist;
    uv.x = xN + 0.5;
    uv.y = yN + 0.5;
    color = tex2D(input, uv);
    return(color);
}

La sintaxis es muy parecida a C, solo que ahora tenemos una función que acepta por parámetro un float2, que es un vector de dos posiciones de tipo float, que además está decorado con TEXCOORD, lo que indica que nos están pasando por parámetro la coordenada x,y del pixel entre 0 y 1, y si nos fijamos la función devuelve un float4, vector de 4 posiciones de tipo float, que está decorado con COLOR, lo que nos indica que tenemos que devolver el color del pixel de esa posición, como bien sabréis son 4 posiciones para RGBA (Red, Green, Blue, Alpha).

Las funciones que se utilizan dentro del cuerpo de la función soy muy sencillas e intuitivas para cualquier programador, así que no las vamos a comentar. La única que merece la pena es tex2D(sampler2D, float2) que acepta por parámetro un sampler, una imagen de origen, y una coordenada x,y y te devuelve el color del pixel en esa posición (float4), esta es la función que tenemos que llamar para saber cuál es el color del pixel del control o UIElement al que le estamos aplicando el efecto.

Como podéis observar esto está definido fuera del cuerpo de la función, en la que tenemos dos variables que son parámetros de entrada para el Pixel Shader, se definen el tipo, el nombre y luego el registro de entrada, en nuestro caso el registro s0 y c0, que dependiendo de la versión del Shader que soporte el hardware tendremos más registros o menos.

Pues bien para poder compilar este fichero tenemos dos opciones, hacerlo con el compilador de Shaders del SDK de DirectX usando este comando:

fxc /T ps_2_0 /E main /Fo<name of HLSL file>.ps <name of HLSL file>.fx

O podemos instalarnos un plug-in de msbuild para que desde Visual Studio compilemos el fichero .fx que contiene el Shader, para hacerlo tenemos que ir a esta web http://www.codeplex.com/wpf/ y descargar “Shader Effects BuildTask and Templates.zip”.

Una vez que tenemos el Pixel Shader compilado lo que tenemos que hacer es generar una clase que sea el ShadeEffect que podremos usar tanto en WPF como en Silverlight 3.0 Beta 1.

Aquí tenemos el ejemplo:

using System;
using System.Windows.Media.Effects;
using System.Windows;
using System.Windows.Media;
namespace MyVideoProject
{
    public class SwirlEffect : ShaderEffect
    {
        private static PixelShader _pixelShader = new PixelShader()
        {
            UriSource = new Uri("MyVideoProject;component/Swirl.ps",
            UriKind.Relative)
        };
        public SwirlEffect()
        {
            PixelShader = _pixelShader;
            UpdateShaderValue(InputProperty);
            UpdateShaderValue(FactorProperty);
        }
        public static readonly DependencyProperty InputProperty =
            ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(SwirlEffect), 0);
        public Brush Input
        {
            get { return (Brush)GetValue(InputProperty); }
            set { SetValue(InputProperty, value); }
        }
        public static readonly DependencyProperty FactorProperty =
            DependencyProperty.Register("Factor",
            typeof(double), typeof(SwirlEffect),
            new PropertyMetadata(0.0, PixelShaderConstantCallback(0)));
        public double Factor
        {
            get { return (double)GetValue(FactorProperty); }
            set { SetValue(FactorProperty, value); }
            }
        }
    }

Lo que estamos definiendo en esta clase es el fichero .ps, que contiene los bytes codes compilados del shader que se ejecutará en la tarjeta gráfica, y además dos Dependency Property especiales que serán los dos parámetros que definimos en el código fuente de nuestro Pixel Shader.

Por un lado se define FactorProperty de tipo double, (r0) y por el otro tenemos un registro del Dependency Property, solo que en vez de llamar a DependecyProperty.Register hemos llamado a ShaderEffect.RegisterPixelShaderSamplerProperty indicando el nombre de la DP y el índice del sampler (s0).

Una vez que se tiene definidos estas dos Dependency Property tenemos que indicar de alguna manera a la clase ShaderEffect que queremos que esos dos valores los actualice desde WPF/SL3.0 al Pixel Shader, eso es lo que se define en el constructor de la clase con las llamadas a UpdateShaderValue(InputProperty) y UpdateShaderValue(FactorProperty).

Así se cierra la definición de un Pixel Shader para su definición, si hubiéramos usado algún otro sampler o registro para nuestros Pixel Shader tendríamos que registrarlo para poder usarlo dentro del Pixel Shader.

Una vez definido todo esto ahora podemos usar nuestro Pixel Shader en WFP como en Silverlight 3.0 Beta 1.

Aquí tenemos un ejemplo de cómo usar nuestro Pixel Shader:

<Border
Margin="24">
    <Border.Effect>
        <local:SwirlEffect
        x:Name="mySwirlEffect"
        Factor="0" />
    </Border.Effect>
<Border>

Y este es el resultado:

clip_image004

Adjunto el proyecto de ejemplo que viene con Silverlight 3.0 Beta 1 Demos. Aquí.

Espero que os guste!!

Luis.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *