アルファブレンドの高速化

戻る

最近、アルファチャンネルの付いた の画像を使用するゲームが増えてきました。

ゲームによっては、スプライトCG(いわゆる立ち絵)の周辺部に、 ゴミが乗っていたりもします。
そのため、Susieプラグインを作成する際も、 画像データのアルファ値を無視できなくなってきました。

アルファ値は、画像の透明度などを表すのに用いられます。
フォトショップなんかだと、いろいろな目的に使うようですが、
ぶっちゃけ、ゲームの画像では前景(立ち絵)と背景を混ぜ合わせる割合にしか使いません。

合成サンプル

この、前景と背景を合成する処理を、αブレンドなんて呼んだりします。
これが意外と重たい処理なんです。
プラグインの処理時間の、実に数十パーセントがこのαブレンドに費やされます。

時間がかかる理由は単純で、「乗算/除算」が必要になるからです。
今の人はあまり知らないかも知れませんが、[乗算/除算」というは、
コンピュータにとっては非常に大変で、「加減算」の数十〜100倍の時間がかかります。

以下は、赤色成分のαブレンドの計算式です(緑、青もいっしょ)

r = {rf×a + rb×(256 - a)}÷256 ・・・ (1)
rf : 前景の赤色成分
rb : 背景の赤色成分
a : 前景のα値

乗算2回、除算1回があります。
1ピクセルがRGBの3成分で構成され、画像のサイズが800×600だとすると、 実に432万回(3×3×800×600)の乗除算が必要なわけです。
そりゃ、時間がかかるわけだ。

上記の式は、実際には以下のように変形して使用します。

r = (rf - rb)×a÷256 + rb ・・・ (2)
掛け算が1回に減りました。
さらに、256で割る部分は8bit右シフトに置き換えられるため、
実際は1回の乗算だけで済みます(一般的に、シフトは加減算並に高速なのです)

これで、計算時間は1/3になったわけですが、実際の所、これでもまだ遅い。
そこで、考えたのが、α値が0や255の時は特別扱いする、というものです。
実際の画像データを見ると、α値が0や255でないのは、
前景と背景の境界部分だけです。
ほとんどのピクセルでは、α値が0や255なのです。

if (a == 0)
	r = rb
else if (a == 0xFF)
	r = rf
else
	r = ((rf - rb)×a >> 8) + rb ・・・ (3)

α値が0や255の時は、全く乗算しなくてもいいわけですから、
かなり速くなります。
とりあえずは、これで十分でしょう。


* これ以上、高速化しようとすると、プラグインの大部分をアセンブラで書くことになるでしょう。
最近のコンパイラは優秀で、部分的にインラインアセンブラを使用しただけでは、
まったく高速化しません。
もはや人間では、コンパイラの最適化に勝てない時代なのです。

描画時のアルファブレンド

リアルタイムでアルファブレンドしながら描画を行う場合は、 また少し状況が変わってきます。多くの場合、背景は固定で、前景の画像を動かしながら、 リアルタイムでアルファブレンドすることになります。

描画段階にならないと合成位置が決まらないため、背景成分は描画時に随時計算するしかありません。 しかし、前景はピクセルごとにα値がわかっています。あらかじめ前景成分を計算しておくことで、 描画を少し高速化することができます。

前景成分(あらかじめ計算)
rf' = rf×a÷256 ・・・ (4)

描画時
r = rf' + rb×(~a) ・・・ (5)

* ~aは、aのビット反転です。(256 - a)でもいっしょ。

* Window98以降の機能ですが、AlphaBlend()という名前のAPIがあります (98のバージョンにはバグがあるらしく、事実上2000以降)
AlphaBlend関数は、アルファブレンドしての描画(BitBlt)をサポートします。 このAPIを使用する場合も、前景成分には(4)式で計算したDIBを指定することになります。

このAlphaBlend()関数、ハードウェアで高速化してくれるのなら使う価値もあるのですが、 内部でソフトウェア処理するらしく、(4), (5)式で計算した場合と描画速度が変わりません(ぉ
OS互換性を考えると、はっきりいって使い物になりません。


戻る inserted by FC2 system