Les shader en flash

Pixel bender toolkit

Adobe permet aux développeurs avertis de créer des filtres personnalisées pour Photoshop et Flash. Sous Flash ces filtres sont utilisables en tant que Shader.

Le fichier généré par Pixel Bender est un fichier binaire et comme le langage utilisé pour générer ce code est du C++, celui-ci a l’énorme avantage d’être moins gourmand en ressource.

Exemple

Télécharger les fichiers de l’exemple

Pour ce tuto j’ai été très inspiré par le tutoriel de ActiveTuts, qui explique comment créer un effet de distorsion d’un écran CRT. Tutoriel que je vous invite à regarder.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<languageVersion : 1.0;>

kernel CRTBuzz
<   namespace : "multifreid";
    vendor : "MultiFreid";
    version : 1;
>
{
    input image4 src;
    output pixel4 dst;
    //Parameter that will move the different channel
    parameter float2 deltaRed;
    parameter float2 deltaGreen;
    parameter float2 deltaBlue;

    void
    evaluatePixel()
    {
        pixel4 red, green, blue;

        red = sampleNearest(src,outCoord()+deltaRed);
        green = sampleNearest(src,outCoord()+deltaGreen);
        blue = sampleNearest(src,outCoord()+deltaBlue);

        dst = red;
        dst.g = green.g;
        dst.b = blue.b;
    }
}

La fonction evaluatePixel() est la fonction qui etre appelé pour produire le pixel final (dst) à partir de l’image source (src).

SampleNearest permet d’effectuer un échantillonnage du pixel de l’image aux coordonnées indiquées en paramètre. Ici on crée un échantillonnage pour chaque décalage passé en paramètre du shader.

Enfin on crée dst à partir de ces trois échantillonnages.

Faites vos tests puis exportez le filtre (File>Export filter for Flash Player…). Pixel Bender  crée alors un fichier .pbj qui est le code binaire de notre filtre.


Dans le code flash il nous faut crée un filtre que l’on pourra utilisé comme DropShadowFilter ou BlurFilter.

Pour cela je crée la classe RGBDistortFilter qui étend ShaderFilter.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package  {
    import flash.display.Shader;
    import flash.filters.ShaderFilter;

    public class RGBDistortFilter extends ShaderFilter {
        public static var theShader:Shader;
        public function RGBDistortFilter(redX:Number=0, redY:Number=0, greenX:Number =0, greenY:Number =0, blueX:Number = 0, blueY:Number = 0) {
            // constructor code
            if(theShader)
            {

                theShader.data.deltaRed.value = [redX, redY];
                theShader.data.deltaGreen.value = [greenX, greenY];
                theShader.data.deltaBlue.value = [blueX, blueY];
                super(theShader);
                this.bottomExtension = this.leftExtension =this.rightExtension = this.topExtension = Math.ceil(Math.max(Math.abs(redX), Math.abs(redY),Math.abs(greenX),
                                                            Math.abs(greenY), Math.abs(blueX), Math.abs(blueY)));
            }
        }
    }
}

Enfin dans la classe Main, j’y fait appel comme suit :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package  {
    import flash.display.MovieClip;
    import flash.events.Event;
    import flash.net.URLLoader;
    import flash.net.URLLoaderDataFormat;
    import flash.net.URLRequest;
    import flash.display.Shader;
    import com.greensock.TweenLite;

    public class Main extends MovieClip{
        var urlLoader:URLLoader;
        var valFilter:Object;

        public function Main() {
            // constructor code
            urlLoader =new URLLoader();
            urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
            urlLoader.addEventListener(Event.COMPLETE, onComplete);
            urlLoader.load(new URLRequest("RGBDistortFilter.pbj"));

             valFilter= new Object();

            valFilter.xr=0;
            valFilter.yr=0;

            valFilter.xg=0;
            valFilter.yg=0;

            valFilter.xb=0;
            valFilter.yb=0;
        }

        function onComplete(evt:Event=null)
        {
            if(evt)
            {
                RGBDistortFilter.theShader = new Shader();
                RGBDistortFilter.theShader.byteCode = evt.currentTarget.data;
            }
            var amplitudeMax:Number = 5;
            TweenLite.to(valFilter, .2, {
                yr: (Math.random()-.5)*amplitudeMax,
                yg: (Math.random()-.5)*amplitudeMax,
                yb: (Math.random()-.5)*amplitudeMax,
                onUpdate:updateFilter,
                onComplete:onComplete});
        }

        function updateFilter()
        {
            var filter: RGBDistortFilter = new RGBDistortFilter(valFilter.xr, valFilter.yr, valFilter.xg, valFilter.yg, valFilter.xb, valFilter.yb);
            titre.filters = [filter];
        }

    }
}

J’utilise la library de tween de greensock pour faire l’animation du filtre. mais il existe de nombreuses techniques pour ça (comme le bruit de Perlin par exemple).