Spécification des valeurs des entrées et des paramètres d’un shader

Flash Player 10 et les versions ultérieures, Adobe AIR 1.5 et les versions ultérieures

De nombreux shaders de Pixel Bender sont définis pour faire appel à une ou plusieurs images utilisées au cours de son traitement. Par exemple, il est courant pour un shader d’accepter une image source et de la produire dotée d’un effet particulier. Suivant l’utilisation du shader, soit la valeur d’entrée est spécifiée automatiquement, soit il faut en fournir une. De la même façon, de nombreux shaders spécifient des paramètres utilisés dans l’individualisation de ce qu’ils produisent. Il faut également définir explicitement une valeur pour chaque paramètre avant d’utiliser le shader.

Vous utilisez la propriété data de l’objet Shader pour définir les entrées et les paramètres et pour établir si un shader en particulier prévoit des entrées et des paramètres. La propriété data est une occurrence de ShaderData.

Identification des entrées et des paramètres d’un shader

La première étape dans la spécification de valeurs d’entrée et de paramètres d’un shader consiste à savoir si celui que vous utilisez prévoit de recevoir des images d’entrée ou des paramètres. Chaque occurrence de Shader possède une propriété data qui contient un objet ShaderData. Si le shader spécifie des entrées ou des paramètres, on y accède en tant que propriétés de cet objet ShaderData. Les noms des propriétés correspondent aux noms spécifiés dans le code source du shader pour les entrées et les paramètres. Par exemple, si un shader spécifie une entrée appelée src , l’objet ShaderData possède une propriété appelée src qui représente cette entrée. Chaque propriété qui représente une entrée est une occurrence de ShaderInput et chaque propriété qui représente un paramètre est une occurrence de ShaderParameter.

Idéalement, le programmeur du shader fournit une documentation pour le shader afin de décrire quels sont les paramètres et les valeurs de l’image d’entrée qu’il prévoit, ce qu’ils représentent, les valeurs appropriées, et ainsi de suite.

Toutefois, si le shader n’est pas documenté et que vous ne disposez pas du code source, vous pouvez inspecter les données du shader pour identifier ces entrées et paramètres. Les propriétés qui représentent ces entrées et paramètres sont ajoutées dynamiquement à l’objet ShaderData. Par conséquent, vous pouvez utiliser une boucle for..in pour inspecter l’objet ShaderData afin de savoir si le shader qui lui est associé spécifie de tels entrées ou paramètres. Comme le décrit la section Accès aux métadonnées du shader , on accède également, en tant que propriété dynamique ajoutée à la propriété Shader.data , à toute valeur de métadonnées définie pour un shader. Lorsque vous utilisez cette technique afin d’identifier entrées et paramètres, vérifiez le type de données des propriétés dynamiques. Si une propriété est une occurrence de ShaderInput, elle représente une entrée. S’il s’agit d’une occurrence de ShaderParameter, elle représente un paramètre. Sinon, il s’agit d’une valeur de métadonnées. L’exemple ci-dessous montre comment utiliser une boucle for..in pour examiner les propriétés dynamiques de la propriété data d’un shader. Chaque entrée (objet ShaderInput) est ajoutée à une occurrence de Vector appelée inputs . Chaque paramètre (objet ShaderParameter) est ajouté à une occurrence de Vector appelée parameters . Enfin, toute propriété de métadonnées est ajoutée à une occurrence de Vector appelée metadata . Remarquez que cet exemple suppose qu’une occurrence de Shader appelée myShader existe déjà :

var shaderData:ShaderData = myShader.data; 
var inputs:Vector.<ShaderInput> = new Vector.<ShaderInput>(); 
var parameters:Vector.<ShaderParameter> = new Vector.<ShaderParameter>(); 
var metadata:Vector.<String> = new Vector.<String>(); 
 
for (var prop:String in shaderData) 
{ 
    if (shaderData[prop] is ShaderInput) 
    { 
        inputs[inputs.length] = shaderData[prop]; 
    } 
    else if (shaderData[prop] is ShaderParameter) 
    { 
        parameters[parameters.length] = shaderData[prop]; 
    } 
    else 
    { 
        metadata[metadata.length] = shaderData[prop]; 
    } 
} 
 
// do something with the inputs or properties

Spécification des valeurs d’entrée d’un shader

De nombreux shaders prévoient de recevoir une ou plusieurs images d’entrée qui sont utilisées au cours de son traitement. Toutefois, dans nombre de cas, une entrée est automatiquement spécifiée lorsque l’objet Shader est utilisé. Par exemple, supposons qu’un shader ait besoin d’une entrée et qu’il serve de filtre. Lorsque le filtre est appliqué à un objet d’affichage ou BitmapData, cet objet est défini automatiquement comme entrée. Dans ce cas, on ne spécifie pas explicitement une valeur d’entrée.

Toutefois, dans certains cas, et plus particulièrement si un shader spécifie plusieurs entrées, il faut définir explicitement une valeur pour l’entrée. Toute entrée définie dans un shader est représentée dans ActionScript par un objet ShaderInput. L’objet ShaderInput est une propriété de l’occurrence de ShaderData dans la propriété data de l’objet Shader (voir Identification des entrées et des paramètres d’un shader ). Par exemple, supposons qu’un shader définisse une entrée appelée src et qu’il soit lié à un objet Shader appelé myShader . Vous accédez alors à l’objet ShaderInput qui correspond à l’entrée src à l’aide de l’identifiant suivant :

myShader.data.src

Chaque objet ShaderInput possède une propriété input qui est utilisée pour définir la valeur de l’entrée. On définit la propriété input sur une occurrence de BitmapData pour spécifier des données d’image. On peut aussi définir la propriété input sur BitmapData ou Vector.occurrence de <Number> pour spécifier des données binaires ou des nombres. Pour plus d’informations sur l’utilisation d’une occurrence de BitmapData ou Vector.<Number> en entrée et les restrictions correspondantes, voir ShaderInput.input dans le Guide de référence ActionScript 3.0 pour la plate-forme Adobe Flash .

En plus de la propriété input , un objet ShaderInput possède des propriétés qui peuvent être utilisées pour déterminer quel type d’image prévoit l’entrée. Ces propriétés contiennent elles-mêmes les propriétés width , height et channels . Chaque objet ShaderInput possède également une propriété index qui est utile afin de déterminer si une valeur explicite doit être fournie pour l’entrée. Si un shader prévoit de recevoir davantage d’entrées que le nombre fixé automatiquement, vous pouvez alors définir des valeurs pour ces entrées. Pour plus de détails sur les différentes façons d’utiliser un shader et pour savoir si oui ou non les valeurs d’entrée sont fixées automatiquement, voir Utilisation d’un shader .

Spécification des valeurs de paramètres dans un shader

Certains shaders définissent des valeurs de paramètres qu’ils utilisent dans la création de leur résultat. Par exemple, un shader qui modifie la luminosité d’une image pourrait spécifier un paramètre de luminosité qui détermine dans quelle mesure l’opération affecte cette luminosité. Un paramètre unique défini dans un shader peut prévoir une ou plusieurs valeurs suivant la façon dont il a été spécifié dans le shader. Tout paramètre défini dans un shader est représenté dans ActionScript par un objet ShaderParameter. L’objet ShaderParameter est une propriété de l’occurrence de ShaderData dans la propriété des données de l’objet Shader. Une description se trouve dans la section Identification des entrées et des paramètres d’un shader . Par exemple, supposons qu’un shader qui définit un paramètre appelé luminosité soit représenté par un objet Shader appelé myShader . Vous accédez alors à l’objet ShaderParameter correspondant au paramètre luminosité à l’aide de l’identifiant suivant :

myShader.data.brightness

Pour spécifier une ou plusieurs valeurs pour le paramètre, créez un tableau ActionScript contenant la ou les valeurs et affectez-le à la propriété value de l’objet ShaderParameter. La propriété value est définie en tant qu’occurrence d’Array car il est possible qu’un seul paramètre du shader nécessite plusieurs valeurs. Même si le paramètre du shader ne prévoit qu’une seule valeur, il vous faut placer la valeur dans un objet Array pour l’affecter à la propriété ShaderParameter.value . Le code suivant montre comment définir une seule valeur pour la propriété value .

myShader.data.brightness.value = [75];

Si le code source Pixel Bender pour le shader spécifie une valeur par défaut pour le paramètre, un tableau contenant la ou les valeurs par défaut est créé et affecté à la propriété value de l’objet ShaderParameter lorsque l’objet Shader est créé. Dès qu’un tableau est affecté à la propriété value , même s’il s’agit de celui par défaut, la valeur du paramètre peut être modifiée en changeant la valeur de l’élément du tableau. Il n’est pas nécessaire de créer un autre tableau et de l’affecter à la propriété value .

L’exemple ci-dessous indique comment définir une valeur de paramètre dans un shader avec ActionScript. Dans cet exemple, le shader définit un paramètre appelé color . Ce paramètre color est déclaré en tant que variable float4 dans le code source Pixel Bender, ce qui signifie qu’il s’agit d’un tableau à quatre nombres en virgule flottante. Dans l’exemple, la valeur du paramètre color change constamment. A chaque changement, le shader est utilisé pour dessiner un rectangle coloré à l’écran. Il en résulte un changement de couleur animé.

Remarque : le code de cet exemple a été rédigé par Ryan Taylor. Merci Ryan de bien vouloir nous en faire profiter. Vous pouvez accéder aux exemples de Ryan et lire ses commentaires à l’adresse www.boostworthy.com/ .

Le code ActionScript repose sur l’utilisation de trois méthodes :

  • init() : dans la méthode init() , le code charge le fichier de pseudo-code binaire Pixel Bender contenant le shader. Lors du chargement du fichier, la méthode onLoadComplete() est appelée.

  • onLoadComplete() : dans la méthode onLoadComplete() , le code crée l’objet Shader appelé shader . Il crée également une occurrence de Sprite appelée texture . Dans la méthode renderShader() , le code dessine une fois par image le résultat du shader dans texture .

  • onEnterFrame() : la méthode onEnterFrame() est appelée une fois par image, créant ainsi un effet d’animation. Dans cette méthode, le code définit la valeur du paramètre du shader sur la nouvelle couleur, puis appelle la méthode renderShader() pour dessiner le résultat du shader sous forme de rectangle.

  • renderShader() : dans la méthode renderShader() , le code appelle la méthode Graphics.beginShaderFill() pour définir un remplissage de shader. Il dessine ensuite un rectangle dont le remplissage est défini par la sortie du shader (la couleur générée). Pour plus d’informations sur ce type d’utilisation d’un shader, voir Utilisation d’un shader comme outil de remplissage de dessin .

Vous trouverez ci-dessous le code ActionScript associé à cet exemple : Utilisez cette classe en tant que classe d’application principale d’un projet ActionScript uniquement dans Flash Builder, ou en tant que classe de document du fichier FLA dans Flash Professional :

package 
{ 
    import flash.display.Shader; 
    import flash.display.Sprite; 
    import flash.events.Event; 
    import flash.net.URLLoader; 
    import flash.net.URLLoaderDataFormat; 
    import flash.net.URLRequest; 
     
    public class ColorFilterExample extends Sprite 
    { 
        private const DELTA_OFFSET:Number = Math.PI * 0.5; 
        private var loader:URLLoader; 
        private var shader:Shader; 
        private var texture:Sprite; 
        private var delta:Number = 0; 
         
        public function ColorFilterExample() 
        { 
            init(); 
        } 
         
        private function init():void 
        { 
            loader = new URLLoader(); 
            loader.dataFormat = URLLoaderDataFormat.BINARY; 
            loader.addEventListener(Event.COMPLETE, onLoadComplete); 
            loader.load(new URLRequest("ColorFilter.pbj")); 
        } 
         
        private function onLoadComplete(event:Event):void 
        { 
            shader = new Shader(loader.data); 
             
            texture = new Sprite(); 
             
            addChild(texture); 
             
            addEventListener(Event.ENTER_FRAME, onEnterFrame); 
        } 
        private function onEnterFrame(event:Event):void 
        { 
            shader.data.color.value[0] = 0.5 + Math.cos(delta - DELTA_OFFSET) * 0.5; 
            shader.data.color.value[1] = 0.5 + Math.cos(delta) * 0.5; 
            shader.data.color.value[2] = 0.5 + Math.cos(delta + DELTA_OFFSET) * 0.5; 
            // The alpha channel value (index 3) is set to 1 by the kernel's default 
            // value. This value doesn't need to change. 
             
            delta += 0.1; 
             
            renderShader(); 
        } 
         
        private function renderShader():void 
        { 
            texture:graphics.clear(); 
            texture.graphics.beginShaderFill(shader); 
            texture.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight); 
            texture.graphics.endFill(); 
        } 
    } 
}

Vous trouverez ci-dessous le code source du noyau du shader ColorFilter, utilisé pour la création du fichier « ColorFilter.pbj » de pseudo-code binaire Pixel Bender.

<languageVersion : 1.0;> 
kernel ColorFilter 
< 
    namespace : "boostworthy::Example"; 
    vendor : "Ryan Taylor"; 
    version : 1; 
    description : "Creates an image where every pixel has the specified color value."; 
> 
{ 
    output pixel4 result; 
     
    parameter float4 color 
    < 
        minValue:float4(0, 0, 0, 0); 
        maxValue:float4(1, 1, 1, 1); 
        defaultValue:float4(0, 0, 0, 1); 
    >; 
     
    void evaluatePixel() 
    { 
        result = color; 
    } 
}

Si vous utilisez un shader dont les paramètres ne sont pas documentés, c’est par le contrôle de la propriété type de l’objet ShaderParameter que vous pouvez évaluer le nombre d’éléments et leur type qu’il faut inclure dans le tableau. La propriété type indique le type de données du paramètre tel qu’il est défini dans le shader lui-même. Pour consulter la liste du nombre et du type d’éléments prévus par chaque type de paramètre, voir le code associé à la propriété ShaderParameter.value dans le Guide de référence ActionScript 3.0 pour Flash Professional.

Chaque objet ShaderParameter possède également une propriété index qui indique l’emplacement du paramètre dans l’ordre des paramètres du shader. En plus de ces propriétés, un objet ShaderParameter peut avoir des propriétés supplémentaires qui contiennent des valeurs de métadonnées fournies par le programmeur du shader. Par exemple, le programmeur peut définir pour un paramètre des valeurs de métadonnées telles que minimum, maximum et des valeurs par défaut. Toutes les valeurs de métadonnées spécifiées par le programmeur sont ajoutées à l’objet ShaderParameter en tant que propriétés dynamiques. Pour passer en revue ces propriétés, utilisez une boucle for..in pour boucler dans les propriétés dynamiques de l’objet ShaderParameter afin d’identifier ses métadonnées. L’exemple ci-dessous montre comment utiliser une boucle for..in afin d’identifier les métadonnées d’un objet ShaderParameter. Chaque valeur de métadonnées est ajoutée à une occurrence de Vector appelée metadata . Remarquez que cet exemple suppose qu’une occurrence de Shader appelée myShader existe déjà et qu’elle possède un paramètre appelé brightness .

var brightness:ShaderParameter = myShader.data.brightness; 
var metadata:Vector.<String> = new Vector.<String>(); 
 
for (var prop:String in brightness) 
{ 
    if (brightness[prop] is String) 
    { 
        metadata[metadata.length] = brightness[prop]; 
    } 
} 
 
// do something with the metadata