Указание значений вводов и параметров шейдера

Flash Player 10 и более поздних версий, Adobe AIR 1.5 и более поздних версий

Шейдер Pixel Bender во многих случаях по определению использует одно или несколько изображений, которые применяются при обработке шейдера. Например, шейдер, как правило, принимает исходное изображение и выводит его, применив тот или иной эффект. В зависимости от способа использования шейдера, вводимое значение может задаваться автоматически или передаваться явно. Подобным образом шейдер часто задает параметры, которые используются для настройки его вывода. Перед использованием шейдера необходимо также явно задать значения для всех параметров.

Чтобы задать вводы и параметры шейдера и определить, ожидает он вводов или параметров, нужно использовать свойство data объекта Shader. Свойство data является экземпляром класса ShaderData.

Определение вводов и параметров шейдера

Прежде чем задавать значения вводов и параметров шейдера, необходимо выяснить, что ожидает используемый шейдер: вводимое изображение или параметры. Каждый экземпляр Shader имеет свойство data , содержащее объект ShaderData. Если шейдер определяет вводы или параметры, их можно получить в качестве свойств этого объекта ShaderData. Имена свойств совпадают с именами вводов и параметров в исходном коде шейдера. Например, если шейдер определяет ввод с именем src , объект ShaderData имеет свойство с именем src , представляющее ввод. Каждое свойство, представляющее ввод, является экземпляром ShaderInput, а каждое свойство, представляющее параметр, — экземпляром ShaderParameter.

В идеале автор шейдера предоставляет его документацию, в которой указано, какие значения вводимых изображений и параметры ожидает шейдер, что они собой представляют, их допустимые значения и т. д.

Однако, если такой документации нет (как и исходного кода), данные шейдера можно проанализировать, чтобы определить его вводы и параметры. Свойства, представляющие вводы и параметры, добавляются в объект ShaderData динамически. Следовательно, объект ShaderData можно проанализировать с помощью цикла for..in , чтобы узнать, определяет ли связанный с ним шейдер какие-либо вводы и параметры. Как описано в разделе « Получение метаданных шейдера », все метаданные, определенные для шейдера, также можно получить в качестве динамического свойства, добавляемого в свойство Shader.data . Используя этот прием для определения вводов и параметров шейдера, проверьте тип данных динамических свойств. Если свойство является экземпляром ShaderInput, оно представляет ввод. Если свойство является экземпляром ShaderParameter, оно представляет параметр. В остальных случаях оно представляет значение метаданных. В следующем примере демонстрируется использование цикла for..in для анализа динамических свойств, содержащихся в свойстве data шейдера. Каждый ввод (объект ShaderInput) добавляется в экземпляр Vector с именем inputs . Каждый параметр (объект ShaderParameter) добавляется в экземпляр Vector с именем parameters . А свойства метаданных добавляются в экземпляр Vector с именем metadata . Обратите внимание, что для этого примера необходимо предварительно создать экземпляр Shader с именем myShader .

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 является свойством экземпляра ShaderData в свойстве data объекта Shader, как описано в разделе « Определение вводов и параметров шейдера ». Предположим, что шейдер определяет ввод с именем src и что он связан с объектом Shader с именем myShader . В этом случае объект ShaderInput, соответствующий вводу src , можно получить с использованием следующего идентификатора:

myShader.data.src

Каждый объект ShaderInput имеет свойство input , с помощью которого задается значение для ввода. Свойству input назначается экземпляр BitmapData, чтобы задать графические данные. Также свойству input можно назначать экземпляр BitmapData или Vector.<Number> для задания бинарных или числовых данных. Дополнительные сведения об использовании экземпляра BitmapData или Vector.Экземпляр <Number> в качестве ввода, см. описание ShaderInput.input в cправочнике ActionScript® 3.0 для платформы Adobe® Flash® Platform .

Помимо свойства input , объект ShaderInput имеет свойства, с помощью которых можно определить, какой тип изображения ожидается в качестве ввода. К их числу относятся свойства width , height и channels . Каждый объект ShaderInput также имеет свойство index , с помощью которого можно определить, должно ли значение ввода передаваться явно. Если шейдер ожидает больше вводов, чем задается автоматически, значит, для оставшихся вводов значения нужно задавать явно. Дополнительные сведения о разных способах применения шейдера и о том, задаются ли значения автоматически, см. в разделе Использование шейдера .

Указание значений параметров шейдера

В некоторых случаях шейдер определяет значения параметров, которые используются для создания вывода. Например, шейдер, изменяющий яркость изображения, может указать параметр, определяющий степень влияния операции на яркость. Один параметр, определенный в шейдере, может ожидать одно или несколько значений в соответствии со своим определением в исходном коде. Каждый параметр, определенный в шейдере, представлен в ActionScript объектом ShaderParameter. Объект ShaderParameter является свойством экземпляра ShaderData в свойстве data объекта Shader, как описано в разделе Определение вводов и параметров шейдера . Предположим, что шейдер определяет параметр с именем brightness и что он связан с объектом Shader с именем myShader . В этом случае объект ShaderParameter, соответствующий вводу brightness , можно получить с использованием следующего идентификатора:

myShader.data.brightness

Чтобы задать одно или несколько значений параметра шейдера, создайте массив ActionScript с одним или несколькими значениями и назначьте его свойству value объекта ShaderParameter. Свойство value определяется как экземпляр Array, так как один параметр шейдера может требовать несколько значений. Даже если параметр шейдера ожидает только одно значение, его необходимо поместить в объект Array, чтобы назначить его свойству ShaderParameter.value . Следующая строка задает одно значение как свойство value .

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

Если исходный код Pixel Bender для шейдера определяет значение по умолчанию для параметра, то создается массив, который содержит одно или несколько значений по умолчанию и назначается свойству value объекта ShaderParameter, когда создается объект Shader. После назначения массива свойству value значение параметра можно изменить, указав другое значение для элемента массива (даже если это массив по умолчанию). Для этого не нужно создавать новый массив и назначать его свойству value .

В следующем примере значение параметра шейдера задается в коде ActionScript. Шейдер определяет параметр с именем color . Параметр color объявляется как переменная float4 в исходном коде Pixel Bender, то есть это массив из четырех чисел с плавающей запятой. В этом примере значение параметра color постоянно изменяется, и при каждом изменении на экране с помощью шейдера рисуется цветной прямоугольник. В результате можно наблюдать анимированное изменение цвета.

Примечание. Код для этого примера написан Райаном Тэйлором (Ryan Taylor). Мы благодарим Райана за предоставление этого кода. Ознакомиться с портфолио и статьями Райана можно по адресу www.boostworthy.com .

Код ActionScript строится на основе трех методов.

  • init() : в методе init() код загружает файл байт-кода Pixel Bender, содержащий шейдер. После загрузки файла вызывается метод onLoadComplete() .

  • onLoadComplete() : в методе onLoadComplete() код создает объект Shader с именем shader . Он также создает экземпляр Sprite с именем texture . В методе renderShader() код рисует результат шейдера в объекте texture по одному разу в каждом кадре.

  • onEnterFrame() : метод onEnterFrame() вызывается один раз для каждого кадра для создания эффекта анимации. В этом методе код задает значению параметра шейдера новый цвет, а затем вызывает метод renderShader() , который рисует на экране вывод шейдера в виде прямоугольника.

  • renderShader() : в методе renderShader() код вызывает метод Graphics.beginShaderFill() , чтобы указать заливку шейдером. Затем он рисует прямоугольник, заливка которого определяется выводом шейдера (сгенерированный цвет). Дополнительные сведения об этом применении шейдера см. в разделе Использование шейдера в качестве заливки .

Ниже приводится код ActionScript для этого примера. Используйте этот класс в качестве основного класса приложения для проекта, созданного только на базе ActionScript в Flash Builder, или в качестве класса документа для FLA-файла в инструменте 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(); 
        } 
    } 
}

Ниже представлен исходный код для ядра шейдера ColorFilter, который использовался для создания файла байт-кода Pixel Bender с именем «ColorFilter.pbj».

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

Если используется шейдер, параметры которого не задокументированы, то можно проверить свойство type объекта ShaderParameter, чтобы выяснить, сколько элементов какого типа должны быть включены в массив. Свойство type указывает тип данных параметра в соответствии с определением шейдера. Список количества и типа элементов, ожидаемых параметрами каждого типа, см. в описании свойства ShaderParameter.value в справочнике ActionScript® 3.0 для Adobe® Flash® Professional CS5.

Каждый объект ShaderParameter также имеет свойство index , которое указывает место данного параметра в ряду всех параметров шейдера. Помимо этих свойств объект ShaderParameter может иметь дополнительные свойства, содержащие значения метаданных, заданные автором шейдера. Например, автор может задать в качестве метаданных минимальное, максимальное значение параметра, а также его значение по умолчанию. Все значения метаданных, заданные автором, добавляются в объект ShaderParameter в качестве динамических свойств. Чтобы проанализировать эти свойства, с помощью цикла for..in пройдите через все динамические свойства объекта ShaderParameter и выявите его метаданные. В следующем примере демонстрируется использование цикла for..in для выявления метаданных объекта ShaderParameter. Каждое значение метаданных добавляется в экземпляр Vector с именем metadata . Обратите внимание, что для выполнения этого примера необходимо предварительно создать экземпляр Shader с именем myShader , который содержит параметр с именем 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