CANDERA開発者コラム
|
はじめに
前回は、アルファブレンドの基本的な考え方と、今回取り扱うアルファブレンドの方法、その式について見てみました。
しかし、前回の結びで、これで全てかというと、そうでもないというお話をしたと思います。今回は、アルファブレンドを考える上で、もう一つの重要な考え方についてご紹介したいと思います。
画像や描画システムにおけるアルファの取り扱い
単純な重ね合わせのブレンドモードである、Src over Dstは、
表1. アルファブレンドの数式で用いる記号
記号 | 意味 |
---|---|
C | カラー値 |
A | アルファ値 |
F | 係数 |
Result | 描画結果 |
Src | これから描画しようとしているもの |
Dst | 描画先 |
上記の定義をベースに、アルファ値は、
$$ A_{Result} = 1.0 * A_{Src} + ( 1.0 - A_{Src} ) * A_{Dst} \tag{1} $$
色の値は、
$$ C_{Result} = ( A_{Src} * C_{Src} * 1.0 + A_{Dst} * C_{Dst} * ( 1.0 - A_{Src} ) ) / A_{Result} \tag{2} $$
と記述することができるというのが、前回の内容でした。この式は描画元と描画先のアルファチャネルの取り扱いによっ
てさらに変化します。そのアルファチャネルの取り扱いの方法には、「ストレートアルファ」と「乗算済みアルファ」の2種
類があります。
ストレートアルファ
文字通り、4チャネル間で特に何かの操作が行われていない状態です。
乗算済みアルファ
予め、RGBのチャネルに、アルファチャネルの値を乗算しておく状態です。予め(Pre)乗算(Multiply)するので、Premultiplied Alpha(プリマルチプライドアルファ)と呼ぶこともあります。
同じような見た目のフレームバッファや画像でも、上記のように二通りの種類があります。描画元と、描画先で、統一されていることもあれば、別々なこともあるとは思います。ただ、どのような形式でも、適切な仕組みを使って描画すれば想定通りの描画結果を得ることはできます。反対に、描画の仕組みを実装する場合、対象のシステムがどうなることを想定としているかを考慮しなければ正しい結果を得ることはできません。では、それぞれの組み合わせで式1、2がどのように変化するか見てみましょう。
アルファブレンドの式の変形
描画先(フレームバッファ、何らかのシステム、オフスクリーン)と描画元(これから描画しようとしている画像など)の関係性により、式1、2の形は変化します。これまでの定義に表2の内容を追加して、数式の変形を試みます。
表2. 数式の追加定義
記号 | 意味 |
---|---|
S | ストレートアルファ |
P | 乗算済みアルファ |
_PP | 描画元が乗算済み、描画先も乗算済み |
_SP | 描画元がストレート、描画先が乗算済み |
_SS | 描画元がストレート、描画先もストレート |
_PS | 描画元が乗算済み、描画先がストレート |
描画元も描画先も乗算済みの場合(PPタイプ)
描画元も描画先も乗算済みである場合がこのタイプに相当します。このタイプについてはあまり深く考えずに合成しても大丈夫です。描画元、描画先がどちらも乗算済みであることで、式が簡略化されます。最終結果の色の部分の計算について、求めるべきものは乗算済みアルファですので、式2に$A_{Result}$を乗算します。このためアルファ値については式1の内容となりますが、色の部分については、式3のようになります。
$$ C_{Result\_PP} = ( A_{Src} + C_{Src} + ( 1.0 - A_{Src} ) * A_{Dst} * C_{Dst} ) )\tag{3} $$
描画元、描画先、共に乗算済みの色であるため、下記の定義を使うことで、式4が得られます。
$$
\begin{align}
C_{Src\_PP} &= A_{Src} * C_{Src} \\
C_{Dst\_PP} &= A_{Dst} * C_{Dst}
\end{align}
$$
$$ C_{Result\_PP} = 1.0 * C_{Src\_PP} + ( 1.0 - A_{Src} ) * C_{Dst\_PP}\tag{4} $$
式1と式4を見比べてみてください。それぞれの要素にかかる係数が、$1.0$や、$(1.0 - A_{Src})$に統一されています。
描画元がストレートで、描画先が乗算済みの場合(SPタイプ)
描画元がストレートアルファで、描画先が乗算済みな場合がこのタイプに相当します。このタイプはいくつか対処方法があります。
一番簡単な方法は、ストレートアルファの画像データをメモリに読み込んだ際に、乗算済みに変換してしまう方法です。ただしこれは、画像のサイズ分のメモリの走査と編集が伴いますので、メモリの読み書きに弱い環境ですと、ボトルネックになり得ます。この変換がコスト的に許容されている場合は、PPタイプの計算方法を適用することができます。
次に考えらえる方法は、描画時に処理してしまうやり方です。例えばOpenGL ESを利用している場合、描画元のアルファ値をフラグメントシェーダ内で乗算します。これも最終的にはPPタイプ同様の計算を適用することになりますが、フラグメントシェーダ内で、フラグメント毎にアルファの乗算が入りますので、シェーダの処理速度があまり良くない環境ですと、負荷のかかる処理となる可能性があります。
事前にPPタイプに誘導できない場合は以下のブレンドの式を適用することになります。最終結果の色の部分の計算については、PPタイプ同様、
$$ C_{Result\_SP} = ( A_{Src} + C_{Src} + ( 1.0 - A_{Src} ) * A_{Dst} * C_{Dst} ) )\tag{5} $$
となります。
描画元がストレートアルファ、描画先が乗算済みであるため、下記の定義を使うことで、式6が得られます。
$$
\begin{align}
C_{Src\_SP} &= C_{Src} \\
C_{Dst\_SP} &= A_{Dst} * C_{Dst}
\end{align}
$$
$$ C_{Result\_SP} = A_{Src} * C_{Src\_SP} + ( 1.0 - A_{Src} ) * C_{Dst\_SP}\tag{6} $$
式1と式6を見比べると、描画元にかかる係数が異なっていることが分かります。
描画元も描画先もストレートな場合(SSタイプ)
稀にではありますが、描画先の種別として、ストレートアルファしか受け付けないケースがあります。この場合の式については式2を変形することができず、
$$ C_{Result\_{SS}} = ( A_{Src} * C_{Src} * 1.0 + A_{Dst} * C_{Dst} * ( 1.0 - A_{Src} ) ) / A_{Result} \tag{7} $$
そのままの形となります。色の値の方もそのまま使うしかないため、
$$
\begin{align}
C_{Src\_SS} &= C_{Src} \\
C_{Dst\_SS} &= C_{Dst}
\end{align}
$$
であるため、単なる文字式の入れ替えとなり、
$$ C_{Result\_{SS}} = ( A_{Src} * C_{Src\_SS} * 1.0 + A_{Dst} * C_{Dst\_SS} * ( 1.0 - A_{Src} ) ) / A_{Result} \tag{8} $$
このように、描画元、描画先のアルファ値や色の値に、何らかの係数を乗ずるようなきれいな式とするのが難しいことが分かります。また、計算式に$A_{Result}$による除算が存在している以上は、どの項にも、描画元と描画先のアルファ値が影響していることになり、計算が複雑になることが分かると思います。
描画元が乗算済み、描画先がストレートの場合(PSタイプ)
このパターンは描画システムとしてストレートアルファしか受け付けないものの、どうしても乗算済みアルファの画像を使用したい場合、例えば、かつてのプロジェクトで、乗算済みアルファの仕組みにて利用していたリソースをそのまま活用したいなどといった場合が考えられます。一般的に乗算済みアルファの画像データを意図的に生成するということはあまり考えられないと思いますが、万が一このようなケースに遭遇した場合、SSパターン同様、きれいな変形を行うことはできません。
$$ C_{Result\_{PS}} = ( A_{Src} * C_{Src} * 1.0 + A_{Dst} * C_{Dst} * ( 1.0 - A_{Src} ) ) / A_{Result} \tag{9} $$
さらに、以下の定義を利用して、
$$
\begin{align}
C_{Src\_PS} &= A_{Src} * C_{Src} \\
C_{Dst\_PS} &= C_{Dst}
\end{align}
$$
$$ C_{Result\_{PS}} = ( A_{Src} * C_{Src\_PS} * 1.0 + A_{Dst} * C_{Dst\_PS} * ( 1.0 - A_{Src} ) ) / A_{Result} \tag{10} $$
となりますが、式8同様、単純な形にはならないことが分かります。
おわりに
いかがでしたでしょうか?今回は、これから描画しようとしているものと描画先の色とアルファの状態がどうなっているかによって、対応方法が異なるというややこしいお話でした。実際にお使いの組み込みの環境などで、PCで見たときと色味が違うなどという問題があった場合、この考え方に立ち戻ると、どうなるのが正しく、現状がどうであるかというのが見えてきます。例えば、これから描画しようとしている画像を適切な形式に変換する、描画システムのブレンド設定を見直すというのは単一の描画システムのみのアプリケーションでは有効ですし、他のアプリケーションなどと重ね合わせてる場合、ターゲット依存の何らかの処理を呼び出し、描画面の作成方法や描画面同士のブレンドの方法を見直すという対応をすることで正しい結果が得られると思います。半透明を扱っている環境をお使いの場合、これらの考えが問題解決の糸口となるかもしれません。
前回と今回は、数式など、知識に関する部分が多く、なかなかイメージがしづらかったかと思います。これまでの内容を言葉にすると、「半透明をいい感じに重ねる」というのは、「ブレンドモードを正しく設定し、絵素材、描画面のフォーマットに合わせた計算式を選択・設定し、その内容で実装する」ということになります。
これで準備段階は完了しました。次回は実際に半透明の画像を描画するための仕組みを実装していきましょう。単純な重ね合わせをゴールとしますので、Src over Dstのブレンドモーを実装します。また、計算の簡略化のため
に、描画面と使用する絵素材は、乗算済みアルファのものとします。画像のコンバート環境とマクロに大きな改修が入りますので、お楽しみにお待ちください。
◆CANDERA(カンデラ)とは?
加賀FEI株式会社では、CGI StudioとUI Conductorという2つの組み込み向けHMIオーサリングツールを開発~販売しており、車載向けメータークラスター、ヘッドアップディスプレイ、ナビゲーション、周辺監視システム、車載以外にもプリンター、デジタルカメラ、建設機械など、多くの機器で広くご採用頂いております。
◆バックナンバー
第1回 身の回りにあるHMI
第2回 OpenGL ESを用いた簡単な図形の描画
第3回 OpenGL ESを用いた簡単な図形の描画 実践編(前編)
第4回 OpenGL ESを用いた簡単な図形の描画 実践編(後編)
第5回 OpenVGを用いた簡単な図形の描画
第6回 ソフトウェアレンダリングを実装してみる(設計編)
第7回 ソフトウェアレンダリングを実装してみる(実装編)
第8回 VBAでテクスチャを描画してみる(前編)
第9回 VBAでテクスチャを描画してみる(後編)
第10回 VBAのテクスチャ描画システムを拡張する(1)
第11回 VBAのテクスチャ描画システムを拡張する(2) ~アルファブレンド(前編)~