BlendStateとアルファブレンディング
About
BlendStateは、レンダリングの際にそれがどのように描画されるかを定義するパラメータ(設定)です。XNAでは標準で用意されるBlendStateの設定を利用すれば大抵のことは対応できるのですが、ここでは一度噛み砕いて、BlendStateを独自に設定できるように解説しようと思います。
Spriteの描画を行う場合と、ポリゴン:特にテクスチャが与えられる場合の描画を行う場合と、それぞれ見た目の結果が異なります。BlendStateの扱いについては共通です。この記事では途中までSpriteを対象に想定して解説していますが、テクスチャが与えられたポリゴンを描画した場合の結果についてもこの記事で言及しています。
DestinationとSource
まず「Destination(目的地,行く先)」と「Source」の概念について解説します。まずSourceとは、これからレンダリングする色(あるいはそのもの)です。例えばテクスチャやポリゴンになります。これに対し、Destinationはレンダリング先の色(あるいはそのもの)です。図では青い画面(Destination)に太陽の絵(Source)を足して、最終的な結果を得ています。またこの結果(Result)に対して例えば雲の画像を追加しようとすると、今度はその結果画像がDestinationとなり、雲の画像がSourceとなります。
ここでは説明のため「テクスチャ(ポリゴン)と画面」としましたが、実際には、それぞれをピクセル単位で合成していることに注意してください。つまり、これから描画されるあるピクセルAの色がSouceであり、これから描画されるピクセルに描画済みの色がDestinationです。
BlendStateの計算式
描画結果は、DestinationとSourceの混ぜ合わせ方によって変わります。例えばデフォルトの状態では、背景の上に先の図のように太陽の図を重ねても、背景の色は太陽の上に滲み出て描画されることはありません。この設定を変更するのがBlendStateです。例えば、描画する物体を半透明にし、背景の色を僅かに混ぜるような設定にすることが可能です。BlendStateの設定は主に3つの値から定義されます。
- BlendFunction(計算の方法)
- DestinationBlend(描画先の色の配合度)
- SourceBlend(描画する色の配合方度)
BlendFunction
まずBlendFunctionについて取り上げます。BlendFunctionはDestinationとSourceをどのように混ぜるかを決定するパラメータです。例えば足し算(BlendFunction.Add)や引き算(BlendFunction.Subtract)などがあります。デフォルトは足し算なので、最終的に得られる画面上の色は次の様に求められます。
一方で引き算の場合は次の様になります。
他にMax,Min,ReverseSubtractがあり、それぞれ次の様に計算されます。
Member | Description |
Add | Destination + Source |
Subtract | Source - Destination |
ReverseSubtract | Destination - Source |
Max | Destination か Sourceの大きい方 |
Min | Destination か Sourceの小さい方 |
しかしここでオカシな点があります。元の色と描画する色を足し合わせたら、ここでは図の通り青色とオレンジ色を足すとしたら、少なくともオレンジ色にはならないハズです。しかしプログラムで動かしてみると、確かにオレンジ色になります。この問題は次に解説する「DestinationBlend」と「SourceBlend」で解決されています。
DestinationBlend と SourceBlend
DestinationBlend(BlendState.ColorDestinationBlend)は、Destinationの色がどれだけ配合されるかを乗算によって定義する(=係数)ためのプロパティです。例えば「ColorDestinationBlend = Blend.Zero」がデフォルトで設定されています。これは、Destination の色を0倍する、という設定です。また同様に、ColorSourceBlendは、Sourceの色がどれだけ配合されるかを乗算によって定義するためのプロパティです。デフォルトでは「ColorSourceBlend = Blend.One」が設定されていて、これはSourceの色を1倍する、という設定です。よってBlendFunctionがAddの時に、これらをデフォルトの設定のままに計算しようとすると、次の式のようになります。
すなわち、これから描画される結果は、「既にレンダリングされている色を0倍した色に、これからレンダリングする色を1倍した色を足した結果となる」ということになります。これが標準設定です。だから描画前の色が消えて、見かけ通りの結果となるわけです。これは厳密には次の様な計算になっています(Destinationに注目して説明)。
この計算はSourceでも同様です。このようにRGBA成分それぞれに、Blendで定められた係数が乗算されるというのが正しい仕様です。Blendには他に、次の様なパラメータが存在します。
|
各パラメータに付けられる接尾辞の「d」は「Destination」の成分です。同様に「s」は「Source」の成分です。 「BothInverseSourceAlpha」は「BlendState.SourceBlendプロパティ」にのみ有効なパラメータで「s」と「d」それぞれに影響を及ぼすパラメータでしたが、XNA4.0では廃止されています。 「(Inverse)BlendFactor」は「BlendState.BlendFactorプロパティ」に与えられた色成分を乗算に利用します。 各パラメータの公式の情報は「Blend Enumeration - MSDN」から確認することが出来ます。ただし見た感じ、InverseSourceColorの説明などが間違っています。 |
α値の計算に生じる問題
実は「ColorDestinationBlend」と「ColorSourceBlend」の説明によって再度問題が生じています。デフォルトの設定はDestination(背景色)はBlend.Zeroによって0が乗じられて、実質的には黒(RGBA=0)になります。そしてSource(描画する色)にはBlend.Oneが乗じられて、黒(RGBA=0)の上にSourceの色が追加されます。計算式で表すと次の通りです。
つまり実際には次の図のように、テクスチャの透過部分は黒く描画されないとオカシイわけです。※既にレンダリングされた色(Destination)は、通常は変更されないため、α=0であっても透過として扱われないようです(透過扱いしたとして、その結果描画するべき色の断定ができないので)。
このようにテクスチャの透過部分が黒く描画される問題は、実際にポリゴンに透過度のあるテクスチャを反映した場合に発生します。実際のテクスチャではα=0で確かに透過されている部分なのに、得られた結果では、該当箇所が黒く表示されます。断じて計算ミスではありません。ところがこの問題はスプライトの描画の場合は発生しません。先の項目ではデフォルトの設定で「Destination x Blend.Zero + Source x Blend.One」とされる、と解説しましたが、スプライトの描画の際にはこの設定が適用されていないことに注意してください。
AlphaBlendの設定
先の項目のようにスプライトに問題が生じないのは、スプライトの描画の際(SpriteBatch.Begin)に「AlphaBlend(AlphaBlending/アルファブレンディング)」の設定が標準で行われるためです。「AlphaBlend/AlphaBlending」とは透過率を考慮した描画合成(やその設定)のことを指します。XNAではスプライトの描画しようとすると、BlendStateの各パラメータ値がそれぞれ「ColorSourceBlend = Blend.One」「ColorDestinationBlend = Blend.InverseSourceAlpha」に切り替わります。この描画結果を計算式で表すと次の様になります。
この計算の結果から分かるように、描画される色の透過度は「元々の色と、描画する色とを足して1」となります。つまりα値(透過度)を考慮して二つの色が混じりあった結果となるわけです。この設定は任意に行えるわけですから、ポリゴンの描画の際にこの設定にしてしまえばスプライトと同様に、透過度を考慮した結果が得られます。XNAではこの設定のためのショートカット的プロパティ値が用意されているので、実際には「BlendState bs = BlendState.AlphaBlend」とすれば上記のαブレンドのための設定が一括で行われます。
この「AlphaBlend」の設定の他に、XNAには「加算合成/Additive」や標準の描画方法である「Opaque」が用意されているので、必要に応じて「BlendState Fields - MSDN」を参照して確認してください。BlendStateに関する説明はこれで終了です。
BlendStateの各パラメータを独自に設定して、独特のレンダリング結果が得られるように工夫するなど様々な利用方法が考えられます。
BlendStateを頻繁に切り替えることは推奨されません。切り替える度に負荷がかかるためです。同じ描画方法で処理すべき描画対象は、一度の設定で済ませてしまうのが、パフォーマンスを上げることに繋がります。描画順等の設定はプログラマが任意に管理する以外に術がありません。描画ロジックは慎重に考えて設計されるべきです。
捕捉:ペイントツール等と結果が異なる
XNAやDirectXで行われているアルファブレンドの計算は、実はPowerPointやPhotoshopといったツールと計算方法が異なります。具体的には「描画元dのα値を考慮した合成」を行っています。もしこのようなことが原因で期待される結果が得られないのであれば、次のページを参照すると解決することがあるかもしれません。
References
- 「消えたRenderState」- ひにけにXNA
- 「かんたんXNA4.0 その24 アルファ・ブレンディング」- Memeplexes
- 「かんたんXNA4.0 その25 ブレンド関数」- Memeplexes
- 「Blend Enumeration」- MSDN
- 掲載情報にミスがあると思われます。このページの項目「DestinationBlend と SourceBlend」を参照されたい。
- 「BlendState Fields」- MSDN