ホーム < ゲームつくろー! < DirectX技術編 < もう悩まないテクスチャブレンディング


その4 もう悩まないテクスチャブレンディング


 私がDirectXを触り出して、大きくつまづいたのが「テクスチャステージ」と「テクスチャブレンディング」でした。テクスチャステージとは、テクスチャをブレンドする順番の事です。テクスチャステージごとにテクスチャを割り当てて、それを合成していく。それが「テクスチャブレンディング」です。しかし、これが何だかややこしい。そこでここでは、テクスチャブレンディングを取り上げてみました。


 まず、テクスチャステージについてです。以下の図をご覧下さい。

 私がテクスチャブレンディングで混乱してしまったのは、テクスチャブレンディングというのが「テクスチャ同士の合成なんだ」と考えてしまったからです。違うんですよね。上の図のように、最下段に「テクスチャStage 0」がありまして、ポリゴンにテクスチャを適用した結果、Stage 1には「テクスチャが適用されたポリゴン」が結果として出てくる。それにStage 1でまたテクスチャを貼り付けると、最終的に2枚のテクスチャが貼られたポリゴンが出来上がる。こういう概念だったんです。

 ところでStage 1の合成で
「黒のバックに黄色の星を貼り付けたら、生成ポリゴンの背景は黒じゃないのか?」
と考えてしまいます。ここが面白いところです。通常、テクスチャには「」と「透明度」の2つの要素が含まれていて、Stage 1の黒い部分は「色は黒だけど透明度が100%」に設定されているのです。だから、背景の黒は描画されないわけです。マルチテクスチャブレンディングでは、この透明度を明確に分けていて、それぞれ個別に設定します。ここをまた私は混乱してました。

 図の各ステージ右側のテクスチャを選択するのがIDirect3DDevice9::SetTexture関数です。

HRESULT SetTexture(
DWORD Stage,
IDorect3DBaseTexture9* pTexture
);

Stageはテクスチャステージ、pTextureはテクスチャオブジェクトへのポインタですね。分かりやすいです。

そして、それぞれのステージで「何(色、透明度)をどう合成するか」を決めるのがIDirect3DDevice9::SetTextureStageState関数です。

HRESULT SetTextureStageState(
DWORD Stage,
D3DTEXTURESTAGESTATETYPE Type,
DWORD Value
);

Stageはテクスチャステージです。
Typeは「合成対象」や「合成演算」を決める部分で、D3DTEXTURESTAGESTATETYPE列挙型にその全てが定義されています。沢山あるので、詳しくは後述しますが、ここで「何の設定をしたいのか」を決めます。
ValueTypeによって意味が変わってくる引数です。Typeで色の設定をすると宣言した場合は、ここでどの色を使用するかを決めます。Typeで透明度(α値)の設定を宣言した場合は、何を透明度とするかを決めます。Typeで演算子を設定するとした場合、Valueは「どういう演算子にするか」という事を決めます。これもやっぱり後述しないと良くわかりませんね。

 SetTextureStageState関数はTypeValueの設定方法が肝となります。下の図をご覧下さい。


 1つのステージで合成をする時に、主に決めなければならない項目は上の□の部分なんです。
対象という部分にはテクスチャ(色、透明度)、ポリゴン自体の色、ポリゴンの輝度(スペキュラ色)そして「前の結果」などが入ります。前の結果というのは前のステージでの出力結果の事です。
演算子という部分には加算、減算、乗算など、いわゆる合成方法が入ります。
そして、は目に見える色についての計算を、透明度はどれだけ透明にするかを計算する事になります。これらは別々に合成されステージの合成が終了します。
 この図を見ていると、「テクスチャとテクスチャが合成できそう」と思えてしまいますが、それは出来ません。というのは、ステージに設定できるテクスチャは1枚だけだからです(一番上の図を見てください)。

 さて、では上の図をじっと睨みながら、色々な合成例を試して見ましょう。


@ 単純貼り付け

 SetTexture関数を単純に使用した場合、実は次のような項目がStage 0にデフォルトで設定されます。

Stage 0  引数1 演算子 引数2
色:  テクスチャの色 × ポリゴンの色
透明度:  テクスチャの透明度 引数1使用 -

色の部分は、ポリゴンの色(DIFFUSE)とテクスチャの色を「掛け算」します。なぜ足し算ではないのか?それはポリゴンの色が白(0xffffffff)の場合、掛け算にするとテクスチャの色がそのまま表示されるからです。ここでいう掛け算とは「色の乗算」というもので、次のような式になっています。

出力R = テクスチャのR × ポリゴンのR / 255
出力G = テクスチャのG × ポリゴンのG / 255
出力B = テクスチャのB × ポリゴンのB / 255

ポリゴンRGBの色味と白色(255,255,255)との比がテクスチャRGBに掛け算された結果が画面に出る事になります。ですから、白いポリゴンだとテクスチャの色がそのまま出るのです。ですから、ポリゴンに色味があると、テクスチャの色も変わります

透明度はデフォルトでテクスチャの透明度をそのまま扱います。演算子の「引数1使用」というのは、演算を行わずにどちらかの値をそのまま使用するというもので、Direct3Dに用意されています。

この設定をSetTextureStageState関数にあえて設定すると、次のようになります。

SetTextureStageState(0, D3DTSS_COLORARG1 , D3DTA_TEXTURE );
SetTextureStageState(0, D3DTSS_COLOROP , D3DTOP_MODULATE ),
SetTextureStageState(0, D3DTSS_COLORARG2 , D3DTA_DIFFUSE ),
SetTextureStageState(0, D3DTSS_ALPHAARG1 , D3DTA_TEXTURE ),
SetTextureStageState(0, D3DTSS_ALPHAOP , D3DTOP_SELECTARG1 ),

上3段が色、下2段が透明度の設定で、上から引数1、演算子、引数2の順になっています。
D3DTSS_COLORARG1というのが「色(Color)の引数(Argument)1」、D3DTSS_COLOROPが「色の演算子(Operator)」、D3DTSS_COLORARG2は色の引数2の設定をしますと宣言しているわけです。これらの設定フラグはマニュアルのD3DTEXTURESTAGESTATETYPE列挙型にすべて書かれています。第3引数は対象及び演算子です。対象についてはD3DTAに、演算子についてはD3DDOPに網羅されています。1行目は対象としてテクスチャ(D3DTA_TEXTURE)、2行目の演算子は掛け算(D3DTOP_MODULATE)を設定しています。3行目のD3DTA_DIFFUSEはポリゴンの色(Diffuse)の事です。
下2行の第2引数も、その名前から分かるように透明度(Alpha)の設定をすると宣言しています。5行目の第3引数にあるD3DOP_SELECTARG1というのが、テクスチャの透明度をそのまま使いますよという演算子です。よって、透明度の引数2(D3DTSS_ALPHAARG2)の設定はする必要がありません。

どうでしょうか?表との対応がちゃんと取れているのが分かりましたでしょうか?


A ポリゴンの透明度を変える

 次はポリゴン全体の透明度をポリゴンの色(Diffuse)のα値で設定する例です。@の例の透明度の設定の部分で、ポリゴンの色に含まれる透明度を使用します。
 まずは設定表を作ります。

Stage 0  引数1 演算子 引数2
色:  テクスチャの色 × ポリゴンの色-
透明度:  テクスチャの透明度 × ポリゴンの透明度

これをそのまま設定します。

SetTextureStageState(0, D3DTSS_COLORARG1 , D3DTA_TEXTURE );
SetTextureStageState(0, D3DTSS_COLOROP , D3DTOP_MODULATE );
SetTextureStageState(0, D3DTSS_COLORARG2 , D3DTA_DIFFUSE );
SetTextureStageState(0, D3DTSS_ALPHAARG1 , D3DTA_TEXTURE );
SetTextureStageState(0, D3DTSS_ALPHAOP , D3DTOP_MODULATE );
SetTextureStageState(0, D3DTSS_ALPHAARG2 , D3DTA_DIFFUSE );

後はポリゴンの色(α値)を変化させれば、ポリゴン全体の透明度が変わります。


B ポリゴンにシールを貼る

 ポリゴンの一部分にシールを貼るような事はしょっちゅうあります。例えばペットボトルの一部にラベルを貼るとか、壁の一部に落書きを描くなどです。@の単純貼り付けは、テクスチャの透明度がポリゴンの色をすかして背後の色を見せてしまうため、シールを貼ったようにはならないのです。
 テクスチャのα値が0xff(255)の時は、背後の色は見えません。逆にα=0x00(0)の時は、背後の色がそのまま出現します。α=0x80(128)の時はどうなるのでしょうか?テクスチャの色と背後の色が半分ずつ足し算されます。一般には、

出力色 = テクスチャの色×α + ポリゴンの色×(1-α)

といういわゆる「αブレンディング」によって実現できます。実は、これを一発で行う嬉しい演算子がちゃんと用意されています。D3DTOP_BLENDTEXTUREALPHAという演算子です。
 設定表は次のようになります。

Stage 0  引数1 演算子 引数2
色:  テクスチャ1の色 αブレンディング ポリゴンの色
透明度:  - 引数2使用 ポリゴンの色

これを設定は次の通りです。

SetTextureStageState(0, D3DTSS_COLORARG1 , D3DTA_TEXTURE );
SetTextureStageState(0, D3DTSS_COLOROP , D3DTOP_BLENDTEXTUREALPHA );
SetTextureStageState(0, D3DTSS_COLORARG2 , D3DTA_DIFFUSE );
SetTextureStageState(0, D3DTSS_ALPHAOP , D3DTOP_SELECTARG2 );
SetTextureStageState(0, D3DTSS_ALPHAARG2 , D3DTA_DIFFUSE );

これでぺったぺたシールを貼れます(笑)。


C 壁にスポットライト(応用編)

 最後にテクスチャブレンディングの応用です。壁の絵が描かれたテクスチャと丸の中がある色で塗りつぶされているテクスチャがあるとして、これらをポリゴン(板ポリゴン)に重ねます。これはサーチライト効果を演出する常套手段です。
 壁のテクスチャをStage 0に、丸のテクスチャをStage 1に設定します。Stage 0ではポリゴンの色を無視してそのまま貼り付けます。透明度も必要ありませんね。Stage 1ではStage 0の結果に対して丸のテクスチャの透明度を適用させます。丸のテクスチャは丸の外側が黒色で透明度が50%、丸の中は白色で透明度100%です。これはαブレンドをすれば良いですね。
 設定表は次のようになります。

Stage 0  引数1 演算子 引数2
色:  壁テクスチャの色 引数1使用 -
透明度:  壁テクスチャの透明度 引数1使用 -
Stage 1  引数1 演算子 引数2
色:  丸テクスチャの色 αブレンド 前の結果
透明度:  - 引数2使用 前の結果

 よく見れば何をしているかわかります。Stage0ではポリゴンの情報を無視して壁の絵を貼っているだけです。Stage 1は丸テクスチャをαブレンドして光のあたっていない部分を暗く、あたっている部分をその色にしています。透明度は前の結果を使用して、壁自体が透明にならないようにします。

 この設定は次の通りです。

SetTextureStageState(0, D3DTSS_COLORARG1 , D3DTA_TEXTURE );
SetTextureStageState(0, D3DTSS_COLOROP , D3DTOP_SELECTARG1 );
SetTextureStageState(0, D3DTSS_ALPHAARG1 , D3DTA_TEXTURE );
SetTextureStageState(0, D3DTSS_COLOROP , D3DTOP_SELECTARG1 );
SetTextureStageState(1, D3DTSS_COLORARG1 , D3DTA_TEXTURE );
SetTextureStageState(1, D3DTSS_COLOROP , D3DTOP_BLENDTEXTUREALPHA );
SetTextureStageState(1, D3DTSS_COLORARG2 , D3DTA_CURRENT );
SetTextureStageState(1, D3DTSS_ALPHAOP , D3DTOP_SELECTARG2 );
SetTextureStageState(1, D3DTSS_ALPHAARG2 , D3DTA_CURRENT );

 ステージが増えると設定が沢山になり結構大変です。D3DTA_CURRENTというのは前の結果を使いますと言う意味です。

 最後に、透明になったポリゴンを描画する時には、IDirect3DDevice9::SetRenderState関数というレンダリングの状態を設定する関数に対して、

SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

とします。1行目はレンダリングをする時にαブレンドを有効に設定します。2行目と3行目はどのようにブレンドするかを設定したもので、この設定ではいわゆる通常のαブレンドが実現されます。特別なレンダリングを行う場合は、2行目と3行目を違った値に設定する必要があります。


 マルチテクスチャブレンディングを行う上で注意する事があります。それは、ビデオカードによってステージ数が制限されているからです。最近のビデオカードならば4段階くらいはあるのですが、少し前まではせいぜい2段階、下手をするとマルチテクスチャブレンドをサポートしていないビデオカードもありました。ですから、マルチテクスチャブレンドを2段階に抑えておくと機種依存性がある程度下がります。


 テクスチャブレンディングは、多分テクスチャクラスに含まれると思います。「このテクスチャは抜き色用に使いたい」「これはシールだ」と、テクスチャ毎に使い道がだいたい決まっているので、それに見合うテクスチャブレンディングのパラメータを設定する仕様になりそうです。これについては「DirectX クラス構築編」で検討予定です。
 テクスチャブレンディングが自由に出来るようになると、2Dをかなり柔軟に扱う事ができるようになります。しかし、DirectXが苦手としている2Dがあります。それが「フォント」です。これについては次に取り上げたいと思います。