La version 3.5 SP1 de WPF permet d'intégrer des effets pixel shader directement à vos applications. Ces effets, accessibles par le biais de la classe ShaderEffect, permettent d'appliquer des effets visuels puissants et complètement personnalisables sans pour autant détoriorer les performances de l'application. Ceux-ci s'exécutent sur un ou des processeurs dédiés de la carte graphique, plutôt que sur le processeur principal de la machine cliente.
Le projet exemple montre l'utilisation de trois effets : le premier modifie les couleurs, le second le positionnement des pixels, et le dernier utilise BlurEffect - un des deux effets accélérés matériellement (par des shaders également) disponible depuis le SP1.

20080923 Shaders settings

Créér son propre effet n'est pas compliqué, mais Visual Studio n'intégrant pas encore de build action spécifique, un certain nombre d'étapes non triviales est requis :

  1. Installez le SDK DirectX. Celui-ci vous donnera accès au compilateur de shaders Direct3D, FXC
  2. Crééz un fichier texte ANSI avec l'extension .fx. Le Bloc-notes Windows vous permettra de sélectionner l'encodage adéquat (Visual Studio utilise l'UTF-8 par défaut). Ajoutez ce fichier au projet.
  3. Ajoutez un autre fichier texte ayant le même nom, mais avec l'extension .ps. Modifiez la Build Action et donnez lui la valeur Resource
  4. Dans les propriétés du projet, modifiez les évènements pre-build pour y ajouter la compilation de votre fichier .fx :
    (C:\chemin vers DX SDK Utilities)fxc.exe" /T ps_2_0 /E main /Fo"$(ProjectDir)MySimpleShader.ps" "$(ProjectDir)MySimpleShader.fx
    La compilation du fichier .fx produira un fichier binaire .ps (pour Direct3D). Le fichier .fx généré regénèrera le fichier .ps préalablement au build.

20080923 ShaderEffect

Maintenant que le squelette du projet est prêt, il reste à implémenter le code du shader dans le fichier .fx écrit en HLSL qui, une fois compilé, sera exécuté par la carte graphique via Direct3D. Ce langage, ressemblant vaguement au C, met à disposition des instructions dédiées aux calculs graphiques. Le projet joint implémente deux exemples simples et commentés. Notons cependant deux points importants :

  • Les données d'input (basés sur Brush) sont fournies par le biais de registres matériels, déclarés en tête du fichier HLSL

    sampler2D implicitInput : register(s0); // premier registre

    float factor : register(c0); // registre "constants" utilisés pour passer des valeurs

    Le résultat du calcul est défini par la valeur de retour de la fonction main

Il faut ensuite que WPF puisse utiliser le code du fichier .ps généré par FXC. Pour cela, déclarez une classe dérivant de ShaderEffect, et définir le bytecode (le fichier .ps) en tant que valeur de la propriété PixelShader:

class MySimpleShaderEffect: ShaderEffect

  {

    private static PixelShader m_pixelShader = new PixelShader() {

       UriSource =

new Uri(@"pack://application:,,,/CustomShaderTest;component/MySimpleShader.ps")

};

Puis définir l'input visuel et les variables via les registres, et initialiser le tout :

public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(MySimpleShaderEffect), 0);

public static readonly DependencyProperty FactorProperty = DependencyProperty.Register("Factor", typeof(double), typeof(MySimpleShaderEffect),

      new UIPropertyMetadata(0.0, PixelShaderConstantCallback(0)));  // variable non input

public MySimpleShaderEffect()

{

   this.PixelShader = m_pixelShader;

   UpdateShaderValue(InputProperty);

   UpdateShaderValue(FactorProperty);

L'application d'un effet est ensuite très facile et entièrement déclarative :

<Button>

   Avec un shader affectant la couleur uniquement

   <Button.Effect>

      <local:MySimpleShaderEffect Factor="{Binding ElementName=sld,Path=Value}"/>

   </Button.Effect>

</Button>

L'impact visuel des shaders, utilisés avec une modération, ne manqueront pas d'apporter à vos applications une touche dynamique toujours appréciée des utilisateurs. Cela d'autant plus qu'ils le permettent avec une très bonne intégration via les DependencyProperties, et sans grande incidence sur les performances de l'application.

Note: Le projet exemple peut ne pas fonctionner immédiatement, il faudra sans doute modifier le chemin vers fxc.exe dans les build events