前編の続きです。
ではFlashで呼び出してみよう!
ここまでの道のりが長かったですね。安心してください。それまでのほとんどが僕のゴタクです。
僕がはじめるにあたって先ず理解しておかないといけないことが多すぎただけで、ほとんどの人は実際にはここまですいすいと来られたはずです。
PixelBenderのpbjファイルをFlashのActionScriptで使うには二種類の方法があります。
実行時に外部ファイルとして読み込むか、Embedであらかじめ埋め込んでおくかです。
前者はXMLや外部JPEGファイルと同じく、URLLoaderを使って読み込む方法です。
Progressionだとこんな感じで
addCommand( new LoadURL(new URLRequest(Directory.PBJ_DIR + "myPixelBender.pbj"), {dataFormat:URLLoaderDataFormat.BINARY}), function():void{ var t:Command = this as Command; _shader = new Shader(t.latestData); } );
Progressionを介さずに普通にAS3で書く場合も、URLLoaderのdataFormatをちゃんとURLLoaderDataFormat.BINARYにしておけばうまくいくと思います。
こんな感じ。
var loader:URLLoader = new URLLoader(); loader.dataFormat = URLLoaderDataFormat.BINARY; loader.addEventListener(Event.COMPLETE, _onCompleteHandler); loader.load(new URLRequest(Directory.PBJ_DIR + "myPixelBender.pbj")); private function _onCompleteHandler(e:Event):void { var shader:Shader = new Shader(loader.data); }
実際にはエラーハンドリングなんかも行う必要ありますがそれはまた別の話なのでここではあえて触れないでおきます。
Embedであらかじめ埋め込んでおく方法は、こんな感じで
[Embed(source="/../shaders/myPixelBender.pbj", mimeType="application/octet-stream")] private var _myPixelBenderKernel : Class; private var _myPixelBenderShader : Shader;
クラスのインスタンス変数として埋め込んでおいて、適切な場所で
_myPixelBenderShader = new Shader(new _myPixelBenderKernel());
として呼び出してやるやり方です。
実行時に外部ファイルとして読み込むか、Embedであらかじめ埋め込んでおくか、どちらのやり方もメリットデメリットあるので自分で適切だと思う方法で呼び出せばいいのですが、もしその判断が出来ない・迷うようであれば後者のEmbedで埋め込む方法をおすすめしておきます。
シェーダーを外部読み込みにするメリットが思いつかないのであればほとんどの場合外部読み込みのデメリットのほうがEmbedする方法のデメリットを上回るからです。
呼び出したのでどうやって使おう?
2種類の呼び出しの方法のどちらを使っても、ActionScriptではShaderクラスとして読み込まれることがわかりました。
ここでActionScriptリファレンスのShaderクラスとそれに付随するShader何々クラスを一読しておくとだいたい何すればいいかわかるかと思います。
僕みたいにクラスの動作が全て頭に入っているわけではないひよここそ、リファレンスの存在は欠かせません(いまだにArrayにさえ、こんなメソッド無いかな〜とリファレンス引きながら作業してるくらいです)。
FlashがPixelBenderをShaderで読み込んだら、いろいろ出来ることがあるんですが、いまのところ僕はShaderJobを使うことをおすすめします。
理由としてはShaderJobが圧倒的に速いからです。
ブレンドモードとして使ったり、塗りに使ったり、フィルタとして使ったり、も確かに面白いのですがそれらがどうしても最終的にFlashの処理を挟む必要があるのにたいして、ShaderJobは同じ事が出来てしかも完全にFlashと処理が切り離せるので「Flashの処理速度という概念を無かったことに出来る」という冒頭の売り文句が発揮できるからです。
しかもFlashとの同期処理・非同期処理のどちらで処理するかを実行時に選べるのです。
これのおかげでShaderJobを使うことによってFlashの描画処理を徹底的にチューニングすることが出来るようになると思います。僕もまだそこまでには達していませんが、少なくともその入り口には立てた気がします。
ShaderJobを使うには、まず入力側のBitmapDataと出力先のBitmapDataが必要になります。
入力の指定については、PixelBenderのKernelの書き方によって違うのですが、
_shader.data.src.input = _inputBitmapData;
こんな感じで指定してやります。
「src」っていう変数名が、ここがKernelで使ってる変数名によって違うので注意が必要です。
PixelBenderで
input image4 src;
と書かれてあれば
_shader.data.src.input = _inputBitmapData;
と書きますし、もし
input image4 sauce;
だと
_shader.data.sauce.input = _inputBitmapData;
こういう感じでActionScriptを書くようになるかと思います。
あとはShaderJobの処理のさせ方ですが、
ShaderJobのリファレンスを読むとコンストラクタの第二引数に出力先のBitmapDataを指定しないといけないことになっているのがわかります。
そこでこんなふうに
var _shaderJob:ShaderJob = new ShaderJob(_shader, _outputBitmapData);
ShaderJobクラスのインスタンスを生成します。
そのあとおもむろに
_shaderJob.start();
してやってもいいんですが、面白いことにShaderJobはstartする際に処理を非同期で実行するか同期して実行するかを選べます。
//_shaderJob.start(); _shaderJob.start(false);
このいずれの場合も非同期で実行します。
なので
_shaderJob.addEventListener(ShaderEvent.COMPLETE, _onShaderJobCompleteHandler); //_shaderJob.start(); _shaderJob.start(false);
こんな感じでイベントリスナーを登録しておくとシェーダーの処理が完了したタイミングを捕捉できるようになります。
画面の処理のために入力や画面の更新などFlashの他の処理を遅らせたくない場合に、完全に処理を分離できるので非常に効果を発揮します。
_shaderJob.start(true);
こうした場合には同期して実行します。
どのみち同期しててもPixelBenderのShaderJobの処理はほとんどの場合は一瞬で終わるので誤差は一瞬ですが、画面更新のタイミングと処理を完全に同期させておきたい場合に効果的です。
さいごに
書いててほんと自分でもびっくりするくらい長い記事になったのですがどうでしょうか。
ほとんどがPixelBenderの技術の話しではなくてPixelBenderとはどういう動作をするものなのかの概念の話しばかりだったと思います。
それだけ僕にとってPixelBenderは概念的な障壁が高く、でも一旦概念を理解してしまえば技術的にそんなにハードルの高いものではない、むしろとっつきやすく奥の深いとても楽しいものだと言うことが解りました。
それに一旦こうして記事にして書いてしまえば、次からPixelBenderの技術的な記事書くときに「概念についてはこちらを参考にしてください」と言えるので、むしろ今後PixelBenderを研究していく布石になったと思います。
僕がPixelBenderを学びはじめるに当たって参考にしたAS3アニメーションのオライリー本にも書かれてあることですが、PixelBenderはOpenGLのシェーダー言語GLSLに非常によく似た言語で、そのノウハウがだいぶ参考になるかと思います。
僕個人の考えとしては、ブラウザ上での描画に限って言えば並列処理を実現するための方法はさまざまに企画が立ち上がっていますが、3年以上も前から提供されていてしかもFlashさえ走ればブラウザを問わず納品レベルできちんと動くPixelBenderには一日の長があるかと思います。
PixelBenderそのものが意外にもとっつきやすくて奥が深く、それでいてFlashが何十倍も楽しくなる面白い技術なのも今回学ぶに当たって新鮮な驚きでした。
この技術がもっと普及して研究がされたら、もっと面白い使いどころが増えると思いますし今までFlashの処理速度が枷だったことがいろいろと解決する可能性が高いかもしれません。
今回の記事ではあえて書かなかったのですが、僕はPixelBenderをByteArrayとかVectorで処理する方法にも非常に興味を持っています。
この記事がきっかけになって、少しでもPixelBenderの使いどころの上手い人が増えて今まで処理速度がネックになっていた問題が解決できれば、WEBの表現はもう一段階自由になるのではないでしょうか。
そこまでは夢見すぎだとしても、少しでも処理速度による表現の限界を解決するヒントになれば幸いです。
「FlashのひよこがPixel Benderではじめる並列処理入門 後編」への1件のフィードバック