Using a shader as a blend mode
Using a shader as a blend mode is like using other blend modes. The shader
defines the appearance resulting from two display objects being blended together
visually. To use a shader as a blend mode, assign your Shader object to the
blendShader
property of the foreground display object. Assigning a value other
than null
to the blendShader
property automatically sets the display
object's blendMode
property to BlendMode.SHADER
. The following listing
demonstrates using a shader as a blend mode. Note that this example assumes that
there is a display object named foreground
contained in the same parent on the
display list as other display content, with foreground
overlapping the other
content:
foreground.blendShader = myShader;
When you use a shader as a blend mode, the shader must be defined with at least two inputs. As the example shows, you do not set the input values in your code. Instead, the two blended images are automatically used as shader inputs. The foreground image is set as the second image. (This is the display object to which the blend mode is applied.) A background image is created by taking the composite of all the pixels behind the foreground image's bounding box. This background image is set as the first input image. If you use a shader that expects more than two inputs, you provide a value for any input beyond the first two.
The following example demonstrates using a shader as a blend mode. This example uses a lighten blend mode based on luminosity. The result of the blend is that the lightest pixel value from either of the blended objects becomes the pixel that's displayed.
Note: The code for this example was written by Mario Klingemann. Thank you Mario for sharing this example. You can see more of Mario's work and read his writing at www.quasimondo.com/.
The important ActionScript code is in these two methods:
init()
: Theinit()
method is called when the application loads. In this method the code loads the shader bytecode file.onLoadComplete()
: In theonLoadComplete()
method the code creates the Shader object namedshader
. It then draws three objects. The first,backdrop
, is a dark gray background behind the blended objects. The second,backgroundShape
, is a green gradient ellipse. The third object,foregroundShape
, is an orange gradient ellipse.The
foregroundShape
ellipse is the foreground object of the blend. The background image of the blend is formed by the part ofbackdrop
and the part ofbackgroundShape
that are overlapped by theforegroundShape
object's bounding box. TheforegroundShape
object is the front-most object in the display list. It partially overlapsbackgroundShape
and completely overlapsbackdrop
. Because of this overlap, without a blend mode applied, the orange ellipse (foregroundShape
) shows completely and part of the green ellipse (backgroundShape
) is hidden by it:However, with the blend mode applied, the brighter part of the green ellipse "shows through" because it is lighter than the portion of
foregroundShape
that overlaps it:
The following is the ActionScript code for this example. Use this class as the main application class for an ActionScript-only project in Flash Builder, or as the document class for the FLA file in Flash Professional:
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;
}
}
}
The following is the source code for the LumaLighten shader kernel, used to create the "LumaLighten.pbj" Pixel Bender bytecode file:
<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;
}
}
For more information on using blend modes, see Applying blending modes.
Note: When a Pixel Bender shader program is run as a blend in Flash Player or
AIR, the sampling and outCoord()
functions behave differently than in other
contexts.In a blend, a sampling function will always return the current pixel
being evaluated by the shader. You cannot, for example, use add an offset to
outCoord()
in order to sample a neighboring pixel. Likewise, if you use the
outCoord()
function outside a sampling function, its coordinates always
evaluate to 0. You cannot, for example, use the position of a pixel to influence
how the blended images are combined.