指定着色器输入和参数值

Flash Player 10 和更高版本,Adobe AIR 1.5 和更高版本

许多 Pixel Bender 着色器定义为使用一个或多个在着色器处理中使用的输入图像。例如,一种常见情况是,着色器接受一个源图像,然后输出应用了特定效果的该图像。根据着色器的使用方式,可以自动指定输入值,也可能需要显式提供值。同样,许多着色器指定用于自定义着色器输出的参数。在使用着色器之前,还必须为每个参数显式设置一个值。

使用 Shader 对象的 data 属性可以设置着色器输入和参数,还可以确定特定着色器是否需要输入或参数。data 属性是一个 ShaderData 实例。

识别着色器输入和参数

指定着色器输入和参数值的第一步是确定要使用的特定着色器是否需要输入图像或参数。每个 Shader 实例都有一个包含 ShaderData 对象的 data 属性。如果着色器定义了任何输入或参数,则这些输入或参数可作为该 ShaderData 对象的属性进行访问。属性的名称与在着色器源代码中为输入和参数指定的名称相匹配。例如,如果着色器定义了一个名为 src 的输入,则 ShaderData 对象具有一个名为 src 的属性用于表示该输入。每个表示输入的属性都是一个 ShaderInput 实例,而每个表示参数的属性都是一个 ShaderParameter 实例。

理想情况下,着色器的创作者会提供着色器文档,用于指示着色器所需的输入图像值和参数、其所表示的内容以及正确的值等等。

但是,如果着色器不带有文档(并且您没有其源代码),则可以检查着色器数据以确定输入和参数。表示输入和参数的属性会动态添加到 ShaderData 对象。因此,可以使用 for..in 循环检查 ShaderData 对象,以确定其关联着色器是否定义了输入或参数。如访问着色器元数据中所述,为着色器定义的任何元数据值还可以作为添加到 Shader.data 属性的动态属性进行访问。在使用这种方法识别着色器输入和参数时,请检查动态属性的数据类型。如果属性是 ShaderInput 实例,则表示输入。如果是 ShaderParameter 实例,则表示参数。其他情况下,则是元数据值。下面的示例演示如何使用 for..in 循环检查着色器的 data 属性的动态属性。每个输入(ShaderInput 对象)都会添加到一个名为 inputs 的 Vector 实例。每个参数(ShaderParameter 对象)都会添加到一个名为 parameters 的 Vector 实例。最后,所有元数据属性都会添加到一个名为 metadata 的 Vector 实例。请注意,此示例假设已创建了名为 myShader 的 Shader 实例:

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

指定着色器输入值

许多着色器需要一个或多个在着色器处理中使用的输入图像。但是,在很多情况下,输入是在使用 Shader 对象时自动指定的。例如,假定着色器需要一个输入,并且该着色器用作滤镜。在滤镜应用于显示对象或 BitmapData 对象时,该对象会自动设置为输入。在这种情况下,无需显式设置输入值。

但是,在某些情况下,特别是在着色器定义多个输入时,一定要显式设置输入值。在 ActionScript 中,在着色器中定义的每个输入都是由一个 ShaderInput 对象表示的。如识别着色器输入和参数中所述,ShaderInput 对象是 Shader 对象的 data 属性中的 ShaderData 实例的属性。例如,假定着色器定义一个名为 src 的输入,并且该着色器链接到一个名为 myShader 的 Shader 对象。在这种情况下,可使用下面的标识符访问对应于 src 输入的 ShaderInput 对象:

myShader.data.src

每个 ShaderInput 对象都有一个 input 属性,该属性用于设置输入值。将 input 属性设置为 BitmapData 实例可指定图像数据。此外,还可以将 input 属性设置为 BitmapData 或 Vector。<Number> 实例以指定二进制或数字数据。有关使用 BitmapData 或 Vector.<Number> 实例作为输入的详细信息和限制,请参阅用于 Adobe Flash Platform 的 ActionScript 3.0 参考中的 ShaderInput.input 列表。

除了 input 属性之外,ShaderInput 对象还具有可用于确定输入所需图像类型的属性。这些属性包括 widthheightchannels 属性。每个 ShaderInput 对象还有一个 index 属性,该属性用于确定是否必须为输入提供显式值。如果着色器需要的输入数量大于自动设置的输入数量,则需要为这些输入设置值。有关使用着色器的不同方式,以及是否自动设置输入值的详细信息,请参阅使用着色器

指定着色器参数值

某些着色器定义了参数值,着色器在创建其结果时会使用这些参数值。例如,更改图像亮度的着色器可能会指定一个亮度参数,该参数确定操作影响亮度的程度。根据着色器中的参数定义,着色器中定义的单个参数可能需要单个值或多个值。在 ActionScript 中,在着色器中定义的每个参数都由一个 ShaderParameter 对象表示。如识别着色器输入和参数中所述,ShaderParameter 对象是 Shader 对象的 data 属性中的 ShaderData 实例的属性。例如,假定着色器定义一个名为 brightness 的参数,并且该着色器由一个名为 myShader 的 Shader 对象表示。在这种情况下,可使用下面的标识符访问对应于 brightness 参数的 ShaderParameter:

myShader.data.brightness

若要为该参数设置一个(或多个)值,请创建包含这个值(或这些值)的 ActionScript 数组,并将该数组分配给 ShaderParameter 对象的 value 属性。value 属性定义为 Array 实例,是因为单个着色器参数可能需要多个值。即使着色器参数只需要一个值,也必须将该值包含在 Array 对象中才能将它分配给 ShaderParameter.value 属性。下面的代码演示如何将单个值设置为 value 属性:

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

如果着色器的 Pixel Bender 源代码为参数定义默认值,则在创建 Shader 对象时,会创建包含默认值的数组,并将其分配给 ShaderParameter 对象的 value 属性。将数组分配给 value 属性之后(包括该数组是默认数组的情况),可以通过更改数组元素的值来更改参数值。您不需要创建新数组并将它分配给 value 属性。

下面的示例演示如何在 ActionScript 中设置着色器的参数值。在此示例中,着色器定义一个名为 color 的参数。在 Pixel Bender 源代码中,color 参数声明为 float4 变量,这意味着该参数是包含四个浮点数的数组。在该示例中,color 参数值会连续更改,该参数每次更改时,都会使用着色器在屏幕上绘制一个有色的矩形。产生的结果是具有动画效果的颜色更改。

注: 此示例的代码由 Ryan Taylor 编写。感谢 Ryan 分享此示例。若要查看 Ryan 的个人资料并阅读他的文章,请访问 www.boostworthy.com/

这段 ActionScript 代码以下面三个方法为中心:

  • init():在 init() 方法中,代码加载包含着色器的 Pixel Bender 字节代码文件。加载文件后,调用 onLoadComplete() 方法。

  • onLoadComplete():在 onLoadComplete() 方法中,代码创建名为 shader 的 Shader 对象。同时还创建名为 texture 的 Sprite 实例。在 renderShader() 方法中,代码将着色器结果逐帧绘制在 texture 中。

  • onEnterFrame()onEnterFrame() 方法按每帧一次的频率进行调用,用于创建动画效果。在此方法中,代码将着色器参数值设置为新颜色,然后调用 renderShader() 方法将着色器结果绘制为矩形。

  • renderShader():在 renderShader() 方法中,代码调用 Graphics.beginShaderFill() 方法来指定着色器填充。然后,绘制一个矩形,该矩形的填充由着色器输出(生成的颜色)定义。有关以此方法使用着色器的详细信息,请参阅使用着色器作为绘制填充

下面是此示例的 ActionScript 代码。使用此类作为 Flash Builder 中纯 ActionScript 项目的主应用程序类,或者作为 Flash Professional 中 FLA 文件的文档类:

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(); 
        } 
    } 
}

下面是 ColorFilter 着色器内核的源代码,用于创建“ColorFilter.pbj”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; 
    } 
}

如果所使用的着色器的参数没有文档说明,则通过检查 ShaderParameter 对象的 type 属性,便可确定数组中必须包含的元素数量及其类型。type 属性指示着色器本身定义的参数数据类型。有关每种参数类型需要的元素数量和类型的列表,请参阅“ActionScript 3.0 参考”中的 ShaderParameter.value 属性列表。

每个 ShaderParameter 对象还有一个 index 属性,该属性指示参数在着色器参数顺序中的位置。除了这些属性之外,ShaderParameter 对象还可以具有包含由着色器创作者提供的元数据值的其他属性。例如,创作者可以指定元数据值,如参数的最小值、最大值和默认值。创作者所指定的所有元数据值都会作为动态属性添加到 ShaderParameter 对象中。若要检查这些属性,请使用 for..in 循环来循环访问 ShaderParameter 对象的动态属性,以识别其元数据。下面的示例演示如何使用 for..in 循环来识别 ShaderParameter 对象的元数据。每个元数据值都会添加到一个名为 metadata 的 Vector 实例。请注意,此示例假定已创建了一个名为 myShader 的 Shader 实例,并且该实例具有一个名为 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