スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

"ステンシルテスト"をやってみた

【リンク】 ◆解説系TOP◆
今日は、ステンシルテストをやってみました。


ステンシルテストを行うと、
-----------------------------------------------------
ピクセル単位で描画する、描画しない
-----------------------------------------------------
ということが出来るようになります。


DirectXなんかを使って描画をする場合、
普通はクライアント領域全体=描画対象になっていると思うのですが、
この描画対象の領域を変えられるというわけです。


【参考画像】
ステンシル2
※ピクセル単位の描画する、描画しないを切り替えるので、
3Dモデルだろうが2Dポリゴンだろうが、特定の場所からスパッと消えます。

更に、描画対象の領域は、四角ではなく
任意の形に切り取るような事も出来ます。

↓実際に実行してみました。 今回は五角形です。
【参考画像】
ステンシル

左から、通常、ステンシルテスト有り、ステンシルテスト有り、です。

※見ても分かるように、特定の領域だけ描画しないと指定する事も出来ますし、
特定の領域のみ描画すると指定する事も出来ます。


~やり方~
--------------------------------------------------------------------------
【1】デバイスのプレゼンテーションパラメータを設定する。
【2】ステンシルバッファをクリアするように設定する。
【3】ステンシルバッファにシルエットを描画する。
【4】普通に描画する

--------------------------------------------------------------------------


【1】デバイスのプレゼンテーションパラメータを設定する。
まず初めに、DirectXのデバイスを作成する時
色々なプレゼンテーションパラメータを設定する と思うのですが、

その中のEnableAutoDepthStencilの項目をTRUE
AutoDepthStencilFormatの項目をD3DFMT_D24S8に設定します。

設定例↓
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
※TRUEにしておかないと、
D3DFMT_D24S8と設定してもその設定は無視されます。

↓詳細はこのページに書かれてあります。
【参考URL】D3DPRESENT_PARAMETERS (プレゼンテーションパラメータ)


【2】ステンシルバッファをクリアするように設定する。
描画する際には、Clear関数でバッファをクリアしたりすると思うのですが、
この関数で、ステンシルバッファもクリアするように設定します。


やり方は、第3引数にD3DCLEAR_STENCILを追加するだけです。
g_pD3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL, D3DCOLOR_XRGB(0,0,60),1.0f, 0 );


ついでに、関数の詳細。

HRESULT Clear(  
DWORD Count,
const D3DRECT *pRects,
DWORD Flags,
D3DCOLOR Color,
float Z,
DWORD Stencil );


Count・・・pRectsの配列にある矩形の数を指定。
pRects・・・クリアする領域(矩形)を、D3DRECT 構造体の配列のポインタで指定する。 NULLに指定すると、描画領域全体がクリアされる。 (通常はNULL)
Flags・・・クリアする対象のフラグを指定する。
Color・・・レンダリング ターゲットをクリアする色を指定。 自分はD3DCOLOR_XRGB(0,0,60)と指定して、 背景を紺色っぽくしてます。
Z・・・深度バッファに保存する新しい z 値を指定する。 0.0が最も近い距離、1.0が最も遠い距離を示します。
Stencil・・・各ステンシル バッファに保存する整数値を指定する。 通常は0でOK。


Flagsについて。
フラグは、ステンシルバッファレンダリングターゲット深度バッファの3種類あり、
任意の組み合わせで指定できます。
(ただし、必ず1つ以上 指定しなければならない。)

そして、その3種類のフラグについて。
D3DCLEAR_STENCIL・・・ステンシル バッファをクリアし、Stencil の値にする。
D3DCLEAR_TARGET・・・レンダリング ターゲットをクリアし、Colorの色にする。
D3DCLEAR_ZBUFFER・・・深度バッファをクリアして、Zの値にする。


【3】ステンシルバッファにシルエットを描画する。
次にステンシルバッファへ任意の書き込みを行うわけですが、
ステンシルバッファ以外の書き込みは行わないようにします。
(深度バッファ(物体がどのくらいの奥行きにあるか?)を有効にしていると、
深度バッファも書き込まれてしまう。)

「どのピクセルを描画しないようにするか?」を設定できさえすれば良いので、
「物体がどれくらいの奥行きに存在するか?」という情報は要らないのです。

で、Zバッファ(深度バッファ)への書き込みを行わないようにするには
下記の様にします。
g_pD3DDevice->SetRenderState( D3DRS_ZFUNC, D3DCMP_NEVER );

そして、次にステンシルテストを有効にします。
 g_pD3DDevice->SetRenderState( D3DRS_STENCILENABLE, TRUE );
(有効にしておかないと、テストを行ってくれません。)

その後に ステンシルテストの為の設定を行うのですが、 その前に
ここでステンシルテストの詳細について書きます。

ステンシルテストは
--------------------------------------------------
ステンシルマスク
ステンシルバッファの値
ステンシル参照値

--------------------------------------------------
この3つの要素を設定し、それらを比較関数で判定を行います。

式としてはこんな感じ↓
-------------------------------------------------------------------------------
(StencilRef & StencilMask) CompFunc (StencilBufferValue & StencilMask)
-------------------------------------------------------------------------------
【参考URL】ステンシルバッファテクニック

論理積をとったものと、論理積を取ったものの値を比較する、という式になってます。
CompFuncには >、<、=、!=などが入る。

で、式は上記のモノなのですが、通常はStencilMask0xffffffffを指定して、
式を簡略化するっぽいです。
-------------------------------------------------------------------------------
( StencilRef ) CompFunc ( StencilBufferValue )
-------------------------------------------------------------------------------
※マスクとの論理積が消える。

要するに、ステンシル参照値と、ステンシルバッファの値を比較するだけ。
また、ステンシル参照値は自分で勝手に決められる値です。

×××××××××××××××××××××××××××××××××

例えば、ステンシル参照値を0x05にしてモデルを描画する とします。

すると、ステンシルバッファのモデルを書き込んだところは5それ以外は0になります。
※0になるのは、最初にClear関数でステンシルバッファを全部0にクリアしているため。

【参考画像】
ステンシル3JPG

で、ステンシルバッファに描き込んだ後、テクスチャの張った大きなポリゴンを
ステンシル参照値0x03、 比較関数をD3DCMP_LESSと指定して描画するとします。
※D3DCMP_LESS・・・"より小さい"()です。

この場合は、
------------------------------------------------
モデルを描き込んだ所は 3 < 5 (成立する)
モデルが書き込まれなかったところは 3 < 0  (成立しない)

------------------------------------------------
となり、モデルを描き込んだステンシルバッファの部分だけ
3Dモデルやポリゴンが描画される事になります。

【参考画像】
ステンシル4
×××××××××××××××××××××××××××××××××

続き。
ステンシルテストを有効にした後、 ステンシルテストを行う為の各種設定を行います。


//ステンシル参照値の設定
g_pD3DDevice->SetRenderState( D3DRS_STENCILREF, 0x05 ); //ステンシル参照値を0x05にする。

//マスクの設定
g_pD3DDevice->SetRenderState( D3DRS_STENCILMASK, 0xffffffff ); //ステンシルマスクの設定。
g_pD3DDevice->SetRenderState( D3DRS_STENCILWRITEMASK, 0xffffffff ); //ステンシルマスクの設定。

//どんな条件が成立した時にステンシルバッファに書き込むのか? の設定
g_pD3DDevice->SetRenderState( D3DRS_STENCILFUNC, D3DCMP_ALWAYS ); //必ずステンシルバッファに描きこませる、という設定。

//書き込む場合、どんな値を書き込むのか?の設定。
g_pD3DDevice->SetRenderState( D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP ); //ステンシルテストに不合格の時・・・何もしない。
g_pD3DDevice->SetRenderState( D3DRS_STENCILZFAIL, D3DSTENCILOP_REPLACE ); //ステンシルテストに合格、深度テストに不合格の時・・・指定したステンシル参照値を書き込む。
g_pD3DDevice->SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_KEEP ); //ステンシルテストと深度テストに合格の時・・・何もしない。
※ややこしそうに見えますが、とにかくステンシル参照値を書き込むようにしている、と思ってください。

あと、他のフラグのまとめがあるページ↓
【参考URL】D3DSTENCILOP列挙型 (処理方法を定義)
【参考URL】D3DCMPFUNC列挙型 (比較関数を定義)


【4】普通に描画する
ステンシルバッファに書き込み終わったら、
変更した設定を元に戻したりした後、普通に描画します。

まず、マスクは先ほどと同じ0xffffffff

ステンシル参照値は、0x03にします。
(もちろん、他の任意の値でもOK。)

比較関数はLESS( < )にします。
(これも、別の任意の比較関数でもOKです。)


で、最後にZバッファの書き込みステンシルバッファの書き込みについて。
先ほどは、
----------------------
ステンシルバッファ 
Zバッファ  ×

----------------------

と指定していたのですが、今度は、
----------------------
ステンシルバッファ ×
Zバッファ  

----------------------
という風に設定します。
(モデルを描画する際は、奥行きの情報が必要。
  逆に、ステンシルバッファを書き込むつもりは無いので、そのように指定する。)

※記事が冗長になりそうなので、こちらは添付するソースコードを見てみてください。

今回のプログラムはこちら↓
【ソースコード】Sample_StencilTest.txt
※→キーを押すことによって、比較関数を切り替えられます。
( D3DCMP_GREATER ←→ D3DCMP_LESS )
スポンサーサイト

テーマ : ゲーム製作 関連 - ジャンル : ゲーム

コメント
コメントの投稿
管理者にだけ表示を許可する



上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。