大重美幸の「これ見落としてませんか? ActionScript 3.0」

第6回 柔軟・多彩・高速な画像処理を実現するシェーダーフィルタ

今回のテーマは、Flash CS4(Flash Player 10)の新機能として登場した「シェーダーフィルタ」です。そもそもシェーダーフィルタとは何でしょうか。フィルタの種類が1つ増えただけ? 3Dで使う? そういう風に思っているなら大間違い。シェーダーフィルタとは、Pixel Bender Toolkitで作成したシェーダープログラムを読み込んで作るフィルタです。シェーダーフィルタはビットマップ、ベクトル、FLVと表示オブジェクトに適用でき、複雑なエフェクトもリアルタイムで処理する実行速度の速さが特長です。本記事では、Pixel Bender Toolkitの使い方から、シェーダーの適用方法までを解説します。柔軟で多彩な高速シェーダーフィルタを体験してみましょう。

※Pixel Bender Toolkitは、Flash CS4 Professionalのインストール時に自動的にインストールされます。

サンプルファイル : edge_oshige_06_sample_fla.zip (851KB)

シェーダーフィルタを使う

前回、ActionScriptでフィルタを使うための基礎知識を説明しました。話の発端は前々回の宿題のFLVの画像処理はシェーダーフィルタが使ってあるということにさかのぼります。シェーダーフィルタを利用すると様々な画像処理が可能になります。ここでもう1つシェーダーフィルタの例を見てもらいましょう。次のswfにマウスカーソルを乗せて動かすと写真が渦を巻きます。このような画像処理を高速で行えるのがシェーダーフィルタの特長です(このムービーのスクリプトは後述します)。

[sample] twirlShader.swf


マウスカーソルを動かすと写真が渦を巻きます。この映像効果にシェーダーフィルタを使っています

Pixel Bender Toolkitを使う

シェーダーフィルタはShaderFilterクラスで作りますが、前回説明したDropShadowFilter、BlurFilter、GlowFilterなどで作るフィルタと違い、画像処理プログラムを書いた外部ファイル(シェーダバイトコードファイル)を読み込んでフィルタを作ります。シェーダバイトコードファイルはPixel Bender Toolkitを使って作ることができますが、今回はcrossStitch.pbkという既存のプログラム(カーネル)を使います。
まず、Pixel Bender Toolkitを起動しcrossStitch.pbkを開きます。次にFileメニューの[Load Image 1...]を実行してフィルタを適用するイメージファイルを読み込み、[Run]ボタンをクリックしてプログラムを実行します。

[sample] crossStitch.pbk

イメージファイルの読み込み
fig1 crossStitch.pbkを開き、イメージファイルを読み込みます

[Run]ボタン
fig2 [Run]ボタンをクリックして画像処理を実行します

右の欄に表示されたスライドバーをドラッグすると、プログラムで使っているsizeパラメータの値を変更でき、その処理結果が即座に左の画像に反映されます。

sizeパラメータのスライドバー
fig3 sizeパラメータのスライドバーをドラッグして変更すると、その結果がすぐに反映します

Pixel Bender Toolkitでフィルタの画像処理の効果を確認したならば、Fileメニューの[Export Filter for Flash Player...]を実行して、このフィルタをFlashで利用できるように書き出します。ファイルの拡張子は.pbjになります。

Export Filter for Flash Player...
fig4 Fileメニューの[Export Filter for Flash Player...]を実行し、crossStitch.pbjのファイル名で書き出します

シェーダーフィルタの作成とフィルタの適用

では作成したPixel Benderのpbjファイルを使ってムービークリップインスタンスにシェーダーフィルタを適用してみましょう。まず最初にURLLoaderクラスを使って外部ファイルのcrossStitch.pbjを読み込みます。このとき、URLLoaderDataFormat.BINARYに設定するのがポイントです。データの読み込みが完了したならばShaderクラスのshaderインスタンスを作り、そのbyteCodeプロパティに読み込んだpbjファイルのデータを設定します。次にShaderFilterクラスのコンストラクタの引数にshaderインスタンスを指定してフィルタを作ります。最後にムービークリップインスタンスphotoのfiltersプロパティに設定すればフィルタが適用されます。

[sample] crossStitchShader.fla、crossStitch.pbj
pbjファイルを読み込み、シェーダーフィルタを作ってムービークリップインスタンスに適用します。

[SWF(backgroundColor="#FF0000")]
//PixelBenderデータを読み込む
var shaderLoader:URLLoader = new URLLoader();
shaderLoader.dataFormat = URLLoaderDataFormat.BINARY;
shaderLoader.addEventListener(Event.COMPLETE, shaderLoaded);
shaderLoader.load(new URLRequest("crossStitch.pbj"));
//読み込んだShaderのパラメータの初期設定
function shaderLoaded(event:Event):void {
	//利用するShaderオブジェクトを作る
	var shader:Shader = new Shader();
	shader.byteCode = shaderLoader.data;
	//photoにShaderFilterを適用する
	var sdf:ShaderFilter = new ShaderFilter(shader);
	photo.filters = [sdf];
}

赤のステージカラーが隙間から見えている
fig5 読み込んだPixel Benderのpbjファイルからシェーダーフィルタを作ってムービークリップインスタンスに適用します。
赤色に見えるのは赤のステージカラーが隙間から見えているからです

シェーダパラメータの設定

Pixel Bender ToolkitではcrossStitchのsizeパラメータの設定値をスケールバーで変更することができましたが、Flashでpbjファイルを読み込んでシェーダーフィルタを作って適用すると、sizeの値はcrossStitchプログラムで初期値として設定してある5になります。この値にアクセスするには、Shaderオブジェクトのdataプロパティを経由してshader.data.size.valueのように参照します。この値は配列として格納されているので、1個の値でも[3]のように配列で値を設定します。

[sample] crossStitchShader2.fla、crossStitch.pbj
crossStitchシェーダーフィルタのsizeパラメータを3に変更します。

shader.data.size.value = [3];

sizeパラメータの値を3に変更したcrossStitchシェーダーフィルタを適用
fig6 sizeパラメータの値を3に変更したcrossStitchシェーダーフィルタを適用します

最初のマウスで渦を作るサンプルではtwirl.pbjというシェーダーを使っていますが、これにはgaussOrSincradiustwirlAnglecenterという4種類のパラメータがあります。ActionScriptでは最初の3つのパラメータの初期値を設定し、マウス移動のイベントに合わせて渦の中心座標を指定するcenterパラメータの値がマウス座標になるようにしています。

[sample] twirlShader.fla、twirl.pbj
マウス移動に合わせて画像に渦巻きを作ります。

[SWF(backgroundColor="#000000")]
//利用するShaderオブジェクトを作る
var shader:Shader = new Shader();
//PixelBenderデータを読み込む
var shaderLoader:URLLoader = new URLLoader();
shaderLoader.dataFormat = URLLoaderDataFormat.BINARY;
shaderLoader.addEventListener(Event.COMPLETE, shaderLoaded);
shaderLoader.load(new URLRequest("twirl.pbj"));
//読み込んだShaderのパラメータの初期設定
function shaderLoaded(event:Event):void {
	shader.byteCode = shaderLoader.data;
	shader.data.gaussOrSinc.value = [0.0];
	shader.data.radius.value = [60.0];
	shader.data.twirlAngle.value = [180.0];
	stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
}
//マウス移動イベントに合わせて変形の中心座標をマウス座標に設定する
function mouseMoveHandler(eventObj:MouseEvent):void {
	shader.data.center.value = [eventObj.stageX,eventObj.stageY];
	//photoにShaderFilterを適用する
	var sdf:ShaderFilter = new ShaderFilter(shader);
	photo.filters = [sdf];
}

ビットマップデータにシェーダーを適用する

前回、フィルタは表示オブジェクトだけでなくビットマップデータにも適用できることを説明しましたが、シェーダーフィルタもビットマップデータに適用できます。ビットマップデータにフィルタを適用するには、applyFilter()を使います。
次の例ではcrossStitch.pbjを使ってシェーダーフィルタを作り、ライブラリで書き出し指定しているビットマップIMGから作ったビットマップデータbmpdataに適用しています。このときに画像を横4×縦3に分割し、千鳥格子のように交互にsizeパラメータを4または2にしたフィルタを合成しています。

[sample] shaderBitmapdata.fla、crossStitch.pbj
ビットマップイメージにシェーダーフィルタを掛けて合成しています。

[SWF(backgroundColor="#009932")]
//PixelBenderデータを読み込む
var shaderLoader:URLLoader = new URLLoader();
shaderLoader.dataFormat = URLLoaderDataFormat.BINARY;
shaderLoader.addEventListener(Event.COMPLETE, shaderLoaded);
shaderLoader.load(new URLRequest("crossStitch.pbj"));
//読み込んだShaderのパラメータの初期設定
function shaderLoaded(event:Event):void {
	//利用するShaderオブジェクトを作る
	var shader:Shader = new Shader();
	shader.byteCode = shaderLoader.data;
	//ビットマップデータbmpdataにフィルタを適用する
	var bmpdata:BitmapData = new IMG(0,0);
	var w:Number = bmpdata.width;
	var h:Number = bmpdata.height;
	var newBmpdata:BitmapData = new BitmapData(w,h,true);
	for (var i:int=0; i<4; i++) {
		for (var j:int=0; j<3; j++) {
			//書き出し位置の設定を行う
			var rect:Rectangle = new Rectangle(0,0,w/4,h/3);
			var pt:Point = new Point(w/4*i, h/3*j);
			rect.offset(pt.x,pt.y);
			//sizeパラメータの設定を行う
			if ((i+j)%2==0) {
				shader.data.size.value = [4];
			} else {
				shader.data.size.value = [2];
			}
			//シェーダーフィルタを作る
			var sdf:ShaderFilter = new ShaderFilter(shader);
			newBmpdata.applyFilter(bmpdata,rect,pt,sdf);
		}
	}
	//ビットマップデータbmpdataを表示するビットマップbmpを作ります。
	var bmp:Bitmap = new Bitmap(newBmpdata);
	bmp.x=(stage.stageWidth-bmp.width)/2;
	bmp.y=(stage.stageHeight-bmp.height)/2;
	addChildAt(bmp, 0);
}

緑のステージカラーが隙間から見えている
fig7 ビットマップデータに千鳥格子のようにシェーダーフィルタを掛けています。
緑色に見えるのは緑のステージカラーが隙間から見えているからです

前回、前々回の宿題の答え

最後に前回前々回の宿題の解答です。前々回の解答はすでに今回の記事の説明で十分ではないでしょうか。FLVの再生に使用しているFLVPlaybackはDisplayObjectクラスを継承している表示オブジェクトなので、ムービークリップインスタンスなどと同じようにfiltersプロパティを利用できます。前々回のFLVには今回使用したcrossStitchシェーダーフィルタが適用されています。次のスクリプトがシェーダーフィルタを掛ける部分です。

[sample] flv_Quiz.fla、crossStitch.pbj
flvPlayerにシェーダーフィルタを掛けます。

var shaderLoader:URLLoader = new URLLoader();
shaderLoader.dataFormat = URLLoaderDataFormat.BINARY;
shaderLoader.addEventListener(Event.COMPLETE, shaderLoaded);
shaderLoader.load(new URLRequest("crossStitch.pbj"));
//flvPlayerにShaderFilterを適用する
function shaderLoaded(event:Event):void {
	var shader:Shader = new Shader();
	shader.byteCode = shaderLoader.data;
	shader.data.size.value = [3];//crossStitchのsizeをセット
	var sdf:ShaderFilter = new ShaderFilter(shader);
	flvPlayer.filters = [sdf];
}

そして、前回の宿題は写真の輪郭までぼけているのはなぜ?という問題でした。その答えは写真のビットマップデータではなく、ビットマップにフィルタが掛けられているからです。この説明でピンとこない人は、ビットマップとビットマップデータの違いについて復習してください。次がbmpにブラーフィルタを掛けている部分です。

[sample] shaderBitmapdata.fla
ビットマップbmpにフィルタを適用します。

var blf:BlurFilter = new BlurFilter();
blf.blurX=blf.blurY=32;
bmp.filters=[blf];

 

関連情報


大重美幸氏写真大重美幸
(おおしげよしゆき)

日立情報システムズ、コミュニケーションシステム研究所を経て独立。株式会社ロクナナ顧問。執筆、講師、ソフトウェア開発を行う。趣味はサーフィンとジョギング。茅ヶ崎在住。著著は約50冊。
twitter : @oshige@as3note

近著:Adobe Flash CS4 詳細!ActionScript 3.0入門ノート[完全改訂版]ActionScript3.0辞典[FlashPlayer10/9対応] ブラウザで無料ではじめるActionScript 3.0 - it's a wonderfl world