指定著色器輸入和參數值

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 物件) 都會加入至 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 物件是 Shader 物件之 data 屬性中 ShaderData 實體的屬性,如 識別著色器輸入和參數 一節中所述。例如,假設著色器定義了輸入 src ,而該著色器連結至 Shader 物件 myShader 。在此情況下,您要使用下列識別名稱,存取對應於 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 物件也有屬性可用來判斷輸入應該預期收到的影像類型。這些屬性包括 width height channels 屬性。每個 ShaderInput 物件也都有 index 屬性,可用來判斷是否必須為輸入提供明確的值。如果著色器應該會接到比自動設定數目更多的輸入,就要設定這些輸入的值。如需著色器不同使用方式以及是否自動設定輸入值的詳細資訊,請參閱 使用著色器

指定著色器參數值

有些著色器會定義參數值,用於建立其結果。例如,改變影像亮度的著色器可能會指定亮度參數,用來判斷作業影響亮度的程度。在著色器中定義的單一參數依著色器中的參數定義而定,會預期接到單一值或多個值。著色器中定義的每個參數都是在 ActionScript 中以 ShaderParameter 物件來代表。ShaderParameter 物件是 Shader 物件之 data 屬性中 ShaderData 實體的屬性,如 識別著色器輸入和參數 一節中所述。例如,假設著色器定義了參數 brightness ,而該著色器連結至 Shader 物件 myShader 。在此情況下,您要使用下列識別名稱,存取對應於 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 color 參數是在 Pixel Bender 原始碼中宣告為 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 屬性指出著色器本身中所定義參數的資料類型。如需一份每個參數類型應該會接到的元素類型和數目清單,請參閱「Adobe® Flash® Professional CS5 的 ActionScript® 3.0 參考」中 ShaderParameter.value 屬性所列的項目。

每個 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