DirectX再入門7

ステンシルテスト

レンダリング結果」を「ステンシルバッファ」でマスクする機能
 
レンダリング
     |→ステンシルテスト
ステンシルバッファ

何に使えるか?
  • モデルのシルエットなどに基づいて、画像を切り抜く
  • 鏡や水面の表現などが可能
ステンシルバッファ
  • レンダリングターゲットと同じサイズの領域
  • ピクセルあたり、8ビット(256諧調)が割り当てられる
  • ピクセルと同じ値がDirectXGraphicsが自動的に読み出す
ステンシルテスト

ステンシルバッファの内容や条件にしたがって、
ピクセルデータを処理ブロックに送るかどうかを判定するテストのこと。

  • テスト合格→転送する
  • テスト不合格→破棄する
テストの方法
    • ステンシルバッファ値
    • ステンシル参照値
    • ステンシルマスク値
  • 条件
    • 比較関数

 
※ステンシルマスク値は、
ステンシルバッファ値やステンシル参照値を
&でマスクする。(論理積
ただし、通常は0xFFFFFFFFを指定しておく(マスクしないようにする)
 
ということで、ステンシルバッファ値とステンシル参照値を
「比較関数」により、テストする。
(ステンシル参照値には任意の値を設定しておく)
 
比較関数一覧

比較関数意味(ステンシル参照値が左辺)
D3DCMP_LESS
D3DCMP_LESSEQUAL
D3DCMP_GREATER
D3DCMP_GREATEREQUAL
D3DCMP_EQUAL
D3DCMP_NOTEQUAL
D3DCMP_ALWAYS常に合格
D3DCMP_NEVER常に不合格
 

深度テスト

前景にオブジェクトがある場合に、書き込みをスキップするテスト。
テストの順番は、「ステンシルテスト」→「深度テスト」で行われる。
 

レンダリングターゲットのステンシルバッファへの書き込み
  • テスト結果による書き込み設定
    • ステンシルテスト不合格の場合に書き込み(D3DRS_STENCILFAIL)
    • ステンシルテスト合格・深度テスト不合格の場合に書き込み(D3DRS_STENCILZFAIL)
    • ステンシルテスト合格・深度テスト合格の場合に書き込み(D3DRS_STENCILPASS)
  • 値の書き込み設定
    • D3DSTENCILOP_REPLACE:ステンシル参照値とステンシルマスク値の論理積を書き込む
    • D3DSTENCILOP_INCR:すでに書き込まれている値+1
    • D3DSTENCILOP_DECR:すでに書き込まれている値−1
    • D3DSTENCILOP_ZERO:0にする
    • D3DSTENCILOP_KEEP:何もしない

 

プログラムではどうするの?

1.ステンシルバッファを作成する
プレゼンテーションパラメータに以下の値を設定。

D3DPRESENT_PARAMETERS.AutoDepthStencilFormat  = D3DFMT_D24S8;

深度バッファの1ピクセルが24ビット、ステンシルバッファの1ピクセルが8ビット。
2.ステンシルバッファのクリア

IDirect3DDevice9::Clear(
	0, 0,
	D3DCLEAR_STENCIL | D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
	D3DCOLOR_XRGB(0, 0, 0),
	1.0f, 0)));

ステンシルバッファをクリアするには、D3DCLEAR_STENCILが必要となる。
3.深度テストの設定

IDirect3DDevice9::SetRenderState(D3DRS_ZFUNC, D3DCMP_XXX);

4.ステンシルテストの設定

// ステンシルテスト有効化
IDirect3DDevice9::SetRenderState(D3DRS_STENCILENABLE, TRUE);
// ステンシル参照値
IDirect3DDevice9::SetRenderState(D3DRS_STENCILREF, 0xXX);
// 各種マスクの設定
IDirect3DDevice9::SetRenderState(D3DRS_STENCILMASK, 0xffffffff);
IDirect3DDevice9::SetRenderState(D3DRS_STENCILWRITEMASK, 0xffffffff);
// ステンシル比較関数
IDirect3DDevice9::SetRenderState(D3DRS_STENCILFUNC, D3DCMP_XXX);

5.ステンシルテスト結果の設定

// ステンシルテスト不合格
IDirect3DDevice9::SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_XXX);
// ステンシルテスト合格、深度テスト不合格
IDirect3DDevice9::SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_XXX);
// ステンシルテストと深度テストの両方合格
IDirect3DDevice9::SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_XXX);

ステンシルシャドウ

ステンシルバッファで影を描画する方法。

Zファイティング

物体を重ねたときに、計算の誤差により、
双方が隠れたり隠れなかったりしてチラチラする現象。
 
ステンシルテストにより抑制する。

影を描画するには?

影の映し出す「光源」、
影の元となる「物体」、
影が映りこむ「物体」
の3つの関係により発生する。
 
今回は、影が映りこむ「物体」を「平面」とする。
 
平面の場合、影の元となる「物体」を、
光源方向から平面に向けて平たく変形するだけでよいので、処理が楽。
 

DirectXでは、ど〜やるの?
  1. D3DXVECTOR4で光源の方向、種類を指定
  2. D3DXPLANEで影が映りこむ平面を定義
  3. 1と2をD3DXMatrixShadowに渡すと、平面に映った影が取得できる
D3DXPlaneFromPointNormal(D3DXPLANE *plane, D3DXVECTOR3 *p, D3DXVECTOR3 *v)
pに「平面状の任意の点」vに「平面の向き」を指定すると、planeに平面が設定される。
Zファイティングの抑制
  1. バックバッファに影を描画
  2. ステンシルバッファに影のマスク画像を作成
  3. 2を使って、影と重なる部分以外を描画