Starling2.0でカスタムフィルターを作ってみる:ポスタリゼーションフィルタ
Starling2.0から、カスタムレンダリングの仕組みが少し変わりました。カスタムレンダリングの実装方法にはフィルターとメッシュスタイルの2つがあり、一長一短です。公式のガイドにのっとり、作るのが簡単なフィルタの方に手を出してみます。今回は、以前AGALを生で触って勉強していた時に作ったポスタリゼーション(階調飛ばし)のコードをフィルターとして移植してみました。
Starling2.0フィルタの特徴
- 派手なエフェクトを作成できる
- 多重にフィルタを重ねがけできる
- Drawコールが増えるので、処理が重め
- 作るのが(MeshStyleに比べて)簡単
- DisplayObjectだけでなく、DisplayObjectContainerにも適用できる
ざっとこんな感じです。MeshStyleについては別途投稿します。
ポスタリゼーションフィルターの動作サンプル
デモはここにあります。各スライダーを操作して、RGBAチャンネルそれぞれでポスタリゼーションのかかり具合を調整できます。写真の女性は画像処理でおなじみのLenaさん。
画像は左上から、フィルタ無し、ポスタリゼーションフィルタのみ、ポスタリゼーションフィルタを適用してからぼかしフィルタ適用、ぼかしフィルタを適用してからポスタリゼーションフィルタ適用、と、なっています。多重フィルタも正しく動いているようです。(そのあたり考慮せずコードを書いても、かってにそのように動きます。)
ちなみにこの状態のポスタリゼーションは Red:8階調、Green:8階調、Blue:4階調で、MSXという昔のコンピュータのScreen8仕様です。うーん、それっぽい!
スライダーを操作すると、階調の飛ばし具合が変化します。この手のフィルタで透明度までポスタリゼーションをかけられるのは珍しいかもしれない。(しかしその対応が面倒だった。後述。)
ポスタリゼーションフィルタの使い方
各チャンネルの分解能を指定して使います。意味的に最低2、最大256です。
var filter:PosterizationFilter = new PosterizationFilter();
filter.redDiv = 2; // redチャンネルの分解能 意味的にmax256まで
filter.greenDiv = 4; // greenチャンネルの分解能
filter.blueDiv = 8; // blueチャンネルの分解能
filter.alphaDiv = 2 // alphaチャンネルの分解能
dobj.filter = filter;
下記は上記と同じ
var filter:PosterizationFilter = new PosterizationFilter(2,4,8,2);
dobj.filter = filter;
フィルタの大雑把なつくり方
公式のガイドで配布されているコードをひながたに改造していくのが簡単です。
ColorOffsetFilter.as
フィルター利用者とのインターフェースはFragmentFilterを継承したクラスに、フィルターの実装本体はFilterEffectを継承したクラスに書きます。ここが別々になってるのはFilterEffectを再利用しようとかそういう意図がありそうですが、通常は1対1の関係になるので、上記ColorOffsetFilterのように、FilterEffect側はインナークラスで書いてしまえば良いと思います。
今回のポスタリゼーションフィルターのコードはここにあります。上記ColorOffsetFilterを元に作りました。
harayoki.starling.filters.PosterizationFilter
FragmentFilter側ではフィルタのパラメータをそのままFilterEffectに伝えて再描画命令を出しているだけです。ほぼ何もしていません。FilterEffect側には実際の描画AGALコードがあり、フィルタのパラメータを2つの定数としてAGALに送っています。はい、それだけです。ここまでは簡単です。
動作サンプルのコードはここにあります。
ポスタリゼーションフィルターのAGALコード
AGAL部分はいわゆるマシン語なので難解ですが、コツをつかめば書けるようになってきます。途中でレジスタ(変数)の値がどうなっているかわからないので、うまく動かない場合は別途似たようなASコードを疑似的に書いて動作させつつ確認すると楽なようです。
Vertex ShaderのAGALコード
既存のAGALコードをそのまま使います。FilterEffect.STD_VERTEX_SHADERに定数定義されています。Starlingで頂点情報を変更するフィルターを作ることはあまりなさそうので、書かなくて良い、と認識すれば良いです。一応中身は下記のようになっています。
//回転行列を座標に掛け合わせる
"m44 op, va0, vc0"
//カラーはそのままFragment Shaderに受けわたす
"mov v0, va1"
各レジスタが何を意味するのかなど今回は解説しませんが、ここのページIntroduction to AGAL: Part 2に詳しく書いてあります。英語なので、そのうち訳します。
Fragment ShaderのAGALコード
先に書いた通り、アルファチャンネルのポスタリゼーションに対応したので長くなりました。もっと効率の良い書き方はあると思いますが、まあそこはご容赦を。
ポスタリゼーションとしての処理は、一旦RGBA値(0.0~1.0)をN(階調段階数)倍して小数点以下を切り落とし、N-1で割って元に戻すと、うまい具合に階調が飛びます。NではなくN-1で割るのは、小数点以下を切り落とすだけだと、色の成分が下によってしまうからです。N-1で割って元に戻すことで、うまい具合の範囲で階調が同じになります。小数点以下切り落としの命令が見当たらなかったので、2つの命令を組み合わせてそこを実現しています。
// テクスチャカラーをft0に取得するお決まりコード
tex("ft0", "v0", 0, texture) // == ft0, v0, fs0 <2d, linear>
// PMA(premultiplied alpha)演算されているのを元の値に戻す rgb /= a
"div ft0.xyz, ft0.xyz, ft0.www"
// 各チャンネルにRGBA定数値(fc0)を掛け合わせる
"mul ft0, ft0, fc0"
// ft0の小数点以下を破棄 ft1 = ft0 - float(ft0)、ft0 -= ft1
"frc ft1, ft0"
"sub ft0, ft0, ft1"
// 定数ft0を掛けた際より1小さい値が定数1にはいっているので、それで割って戻す
"div ft0, ft0, fc1"
// 1.0を超える部分ができるので正規化 (sat : 0.0~1.0に収める命令)
"sat ft0, ft0"
// PMAをやり直す rgb *= a
"mul ft0.xyz, ft0.xyz, ft0.www"
// ocに出力
"mov oc, ft0"
アルファチャンネルが絡んでくると、公式ガイドにあるように、PMA(premultiplied alpha)を考慮しないといけないので、若干面倒です。できあがれば簡単なコードになりますが、AGALコーディング最中はどこでおかしな結果になっているかわからず、苦労しました。
フィルターのバグ?
動作サンプルを作っているに当たって、1つのフィルターを複数のDisplayObjectに適用すると、DisplayObjectが消えたり、ランタイムエラーが出ることに気づきました。バグなのか仕様なのかわかりませんが、flashネイティブではフィルタを複数のDisplayObjectに適用することが可能なので、直して欲しいですね。今回はフィルターのインスタンスを複数作って対応しました。こちら、問い合わせてみます。
まとめ
世の中にはAGALを簡単に記述するための高級言語やクラスも存在するようですが、今はまず手作業で書きつつ勉強することにしています。AGALについても何本がまとめを投稿する予定です。
なお、StarlingではAGALバージョン1.0を使っています。現在の最新はAGAL3です。AGAL2がデフォルトになる日はいつの日か。 AGAL2は2014/1のリリースなのでそろそろいいんではないかな。。
http://labsdownload.adobe.com/pub/labs/flashruntimes/shared/air16_flashplayer16_releasenotes.pdf
Author And Source
この問題について(Starling2.0でカスタムフィルターを作ってみる:ポスタリゼーションフィルタ), 我々は、より多くの情報をここで見つけました https://qiita.com/harayoki/items/4daa6a3fbcfa115be70a著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .