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.
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 :
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 :
sampler2D implicitInput : register(s0); // premier registre
float factor : register(c0); // registre "constants" utilisés pour passer des valeurs
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