ブレンドモードでのシェーダーの使用

Flash Player 10 以降、Adobe AIR 1.5 以降

ブレンドモードでのシェーダーの使用は、他のブレンドモードを使用する場合と同じです。シェーダーは、視覚的にブレンドされている 2 つの表示オブジェクトから生成される外観を定義します。シェーダーをブレンドモードで使用するには、前景の表示オブジェクトの blendShader プロパティに Shader オブジェクトを割り当てます。 null 以外の値を blendShader プロパティに割り当てると、表示オブジェクトの blendMode プロパティが自動的に BlendMode.SHADER に設定されます。次の例は、シェーダーをブレンドモードで使用する方法を示します。この例では、表示リスト上で他の表示コンテンツと同じ親に含まれている foreground という名前の表示オブジェクトがあり、 foreground が他のコンテンツと重なっていることを前提としています。

foreground.blendShader = myShader;

ブレンドモードでシェーダーを使用する場合は、シェーダーに最低 2 つの入力を定義する必要があります。例に示すように、コードでは入力値を設定しません。その代わり、シェーダーの入力として 2 つのブレンドされたイメージが自動的に使用されています。前景イメージは 2 番目のイメージとして設定されています(これは、ブレンドモードが適用される表示オブジェクトです)。背景イメージは、前景イメージの境界ボックスの後ろのすべてのピクセルを複合して作成されます。この背景イメージは、最初の入力イメージとして設定されています。3 つ以上の入力を使用するシェーダーでは、最初の 2 つ以外の入力値を指定します。

次の例は、シェーダーをブレンドモードで使用する方法を示します。この例では、輝度に基づくより明るいブレンドモードを使用します。ブレンドの結果、ブレンドされたいずれかのオブジェクトの最も明るいピクセル値が、表示されるピクセルになります。

注意: このコード例は Mario Klingemann 氏が作成したものです。この例の掲載を許可してくれた Mario 氏に感謝いたします。Mario 氏のその他の著述については、 www.quasimondo.com/ で読むことができます。

重要な ActionScript コードは、次の 2 つのメソッドにあります。

  • init() :アプリケーションがロードされると、 init() メソッドが呼び出されます。このメソッドで、シェーダーのバイトコードファイルがロードされます。

  • onLoadComplete() onLoadComplete() メソッドで、 shader という名前の Shader オブジェクトが作成されます。次に、3 つのオブジェクトを描画します。第 1 の backdrop は、ブレンドされたオブジェクトの後ろにある濃い灰色の背景です。第 2 の backgroundShape は、緑色のグラデーションの楕円です。第 3 のオブジェクト foregroundShape は、オレンジ色のグラデーションの楕円です。

    foregroundShape 楕円はブレンドの前景オブジェクトです。ブレンドの背景イメージは、 backdrop の部分と backgroundShape の部分から構成され、 foregroundShape オブジェクトの境界ボックスがこれに重なっています。 foregroundShape オブジェクトは、表示リストで最も前面のオブジェクトです。このオブジェクトは、 backgroundShape の一部と backdrop の全体に重なっています。このように重なっているため、ブレンドモードが適用されない場合は、オレンジ色の楕円( foregroundShape )の全体が表示され、緑色の楕円( backgroundShape )の一部がこれによって隠されています。

    ただし、ブレンドモードを適用すると、 foregroundShape の重なった部分よりも明るいために、緑色の楕円のより明るい部分が表示されるようになります。

この例の ActionScript コードは次のようになります。Flash Builder の ActionScript のみのプロジェクトでは、このクラスをメインアプリケーションクラスとして使用します。または、Flash Professional の FLA ファイルでは、ドキュメントクラスとして使用します。

package 
{ 
    import flash.display.BlendMode; 
    import flash.display.GradientType; 
    import flash.display.Graphics; 
    import flash.display.Shader; 
    import flash.display.Shape; 
    import flash.display.Sprite; 
    import flash.events.Event; 
    import flash.geom.Matrix; 
    import flash.net.URLLoader; 
    import flash.net.URLLoaderDataFormat; 
    import flash.net.URLRequest; 
     
    public class LumaLighten extends Sprite 
    { 
        private var shader:Shader; 
        private var loader:URLLoader; 
         
        public function LumaLighten() 
        { 
            init(); 
        } 
         
        private function init():void 
        { 
            loader = new URLLoader(); 
            loader.dataFormat = URLLoaderDataFormat.BINARY; 
            loader.addEventListener(Event.COMPLETE, onLoadComplete); 
            loader.load(new URLRequest("LumaLighten.pbj")); 
        } 
         
         
        private function onLoadComplete(event:Event):void 
        { 
            shader = new Shader(loader.data); 
             
            var backdrop:Shape = new Shape(); 
            var g0:Graphics = backdrop.graphics; 
            g0.beginFill(0x303030); 
            g0.drawRect(0, 0, 400, 200); 
            g0.endFill(); 
            addChild(backdrop); 
             
            var backgroundShape:Shape = new Shape(); 
            var g1:Graphics = backgroundShape.graphics; 
            var c1:Array = [0x336600, 0x80ff00]; 
            var a1:Array = [255, 255]; 
            var r1:Array = [100, 255]; 
            var m1:Matrix = new Matrix(); 
            m1.createGradientBox(300, 200); 
            g1.beginGradientFill(GradientType.LINEAR, c1, a1, r1, m1); 
            g1.drawEllipse(0, 0, 300, 200); 
            g1.endFill(); 
            addChild(backgroundShape); 
             
            var foregroundShape:Shape = new Shape(); 
            var g2:Graphics = foregroundShape.graphics; 
            var c2:Array = [0xff8000, 0x663300]; 
            var a2:Array = [255, 255]; 
            var r2:Array = [100, 255]; 
            var m2:Matrix = new Matrix(); 
            m2.createGradientBox(300, 200); 
            g2.beginGradientFill(GradientType.LINEAR, c2, a2, r2, m2); 
            g2.drawEllipse(100, 0, 300, 200); 
            g2.endFill(); 
            addChild(foregroundShape); 
             
            foregroundShape.blendShader = shader; 
            foregroundShape.blendMode = BlendMode.SHADER; 
        } 
    } 
}

次の例は、「LumaLighten.pbj」という Pixel Bender バイトコードファイルの作成に使用された LumaLighten シェーダーカーネルのソースコードです。

<languageVersion : 1.0;> 
kernel LumaLighten 
< 
    namespace : "com.quasimondo.blendModes"; 
    vendor : "Quasimondo.com"; 
    version : 1; 
    description : "Luminance based lighten blend mode"; 
> 
{ 
    input image4 background; 
    input image4 foreground; 
 
    output pixel4 dst; 
     
    const float3 LUMA = float3(0.212671, 0.715160, 0.072169); 
 
    void evaluatePixel() 
    { 
        float4 a = sampleNearest(foreground, outCoord()); 
        float4 b = sampleNearest(background, outCoord()); 
        float luma_a = a.r * LUMA.r + a.g * LUMA.g + a.b * LUMA.b; 
        float luma_b = b.r * LUMA.r + b.g * LUMA.g + b.b * LUMA.b; 
         
        dst = luma_a > luma_b ? a : b; 
    } 
}

ブレンドモードについて詳しくは、 ブレンドモードの適用 を参照してください。

注意: Flash Player または AIR で Pixel Bender シェーダープログラムをブレンドとして実行する場合、サンプリング関数と outCoord() 関数の動作は他のコンテキストと異なります。ブレンドの場合、サンプリング関数は、シェーダーで評価される現在のピクセルを常に返します。例えば、隣接するピクセルをサンプリングするために outCoord() にオフセットを追加することはできません。同様に、サンプリング関数の外で outCoord() 関数を使用すると、その座標は常に 0 に評価されます。例えば、ピクセルの位置を使用して、ブレンドされたイメージの結合方法を制御することはできません。