ステンシル


概要

ステンシルピクセル単位で参照値と呼ばれる値を設定し、
その値に基づいてピクセルの描画を行うかどうかは判断する描画手法のことです。
ステンシルを使用すればオブジェクト部分のみをくりぬいた描画を行う事などができます。
このピクセル単位の値を保存する領域をステンシルバッファ、
ステンシルバッファに保存され、ピクセルの描画のテストに使用される値を参照値、
参照値を使用したテストをステンシルテストと呼ばれています。

gmpg_0118

サンプル

サンプルはここからダウンロードでき、環境については以下の内容となっています。

開発環境
VSのバージョン DirectXのバージョン
VisualStudio 2017 DirectX9(June 2010)

実用例

ステンシルを使用すれば以下のように壁にシルエットが写っているような
演出を行うことも可能です。

gmpg_0220

ステンシルバッファ

ステンシルバッファは参照値を保存するためのバッファです。
基本的に描画対象となるフレームバッファと同じ大きさを用意します。
各ピクセルに対して割り当てられているサイズは設定で変更できますが
1バイトのケースが多いです。
1バイトの場合は0~255までの256段階の参照値の設定が可能ということになります。
DirectX9ではZバッファのメモリとステンシルバッファのメモリは同じ領域に用意されています。

gmpg_0119

ステンシルテスト

ステンシルテストはフレームバッファに新しく描き込むピクセルの参照値と
ステンシルバッファの参照値を利用して行う描き込み判定処理のことです。

gmpg_0123

参照値

ステンシルテストは新しく描画を行う予定のピクセルの参照値と
ステンシルバッファの参照値を使用して比較を行います。

マスク

ステンシルバッファ側の参照値はピクセルの参照値と比較する前に
マスクをかけることができるので、バッファ側の参照値をコントロールして
テストを行うことが可能です。

判定方法

判定は「A == B」や「A > B」等といったプログラムでよく使用する方法で行います。
また、判定を「必ず成功 or 失敗」させることも可能です。

gmpg_0120

結果

ステンシルはステンシルテストの結果とZテストの結果の組み合わせで、
ステンシルバッファへ反映するかどうかを決めることができます。

テスト結果の組み合わせ
ステンシルテスト Zテスト 内容
両方のテストが合格
× ステンシルのみ合格
× × 両方のテストが不合格
このテスト結果はどれか1つを選択して反映方法を決めるのではなく、 全てのケースに対してどのように反映するのかを決めることができます。
反映内容
書き込まない
ピクセルの参照値に変更
ステンシルバッファの値に+1
ステンシルバッファの値に-1
ビットを反転する

DX9によるステンシルの実装方法

DirectX9を使用したステンシル処理の実装方法紹介します。

プレゼントパラメータの設定を変更する

ステンシル機能を使用するためにはDirectGraphicsのデバイスを生成する際に
使用するプレゼントパラメータの設定が必要です。

// ステンシル用設定
// 深度バッファの有無
present_param->EnableAutoDepthStencil = TRUE;
// 深度バッファのフォーマット
present_param->AutoDepthStencilFormat = D3DFMT_D24S8;

ステンシルの設定では「EnableAutoDepthStencil」と
「AutoDepthStencilFormat」の値を変更します。

EnableAutoDepthStencilはステンシル、デプス機能を有効する変数で、
AutoDepthStencilFormatはステンシルとデプスのバッファフォーマットを決める変数です。
今回の「D3DFMT_D24S8」はバッファの1つのデータを32ビットで扱い、
デプスが24ビット、ステンシルが8ビットで管理する設定になります。

ステンシルバッファをクリア

DirectX9ではフレームバッファのクリアを行っているClear関数がありますが、
そこで、ステンシルバッファのクリアも行います。
(Zバッファのクリアを設定しない場合はそちらも合わせて行ってください)

g_pD3DDevice->Clear(
	0,
	nullptr,
	D3DCLEAR_TARGET | D3DCLEAR_STENCIL | D3DCLEAR_ZBUFFER,	// 初期化するバッファの種類
	D3DCOLOR_ARGB(255, 255, 0, 0),				// フレームバッファの初期化色
	1.0f,							// Zバッファの初期値
	0);							// ステンシルバッファの初期値

ステンシルバッファ側の設定

ステンシルでは型抜きの役割をするステンシルバッファの設定と
その型を使ってオブジェクトを描画する場合では設定が異なるので、
まずはステンシルバッファ側(型抜き側)の設定から紹介します。
※今回のステンシルバッファの設定は描画を行わず、
ステンシルバッファに書き込む方法の紹介です。

Zテストの設定

マスク側のZテストの設定は「テストを有効にする」「テストを必ず失敗させる」を行います。
テストを有効にする
ステンシルにはZテストが必要となりますので、Zテストを有効にします。


// Zテスト設定 => 有効
g_pD3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE);

Zテストを必ず失敗させる
マスクはフレームバッファに反映させる必要はありません。
なので、Zテストの設定を必ず失敗するようにして、ステンシルバッファにだけ
テスト結果を反映させるようにします。

// ZBUFFER比較設定変更 => 必ず失敗する
g_pD3DDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_NEVER);

ステンシル設定

マスク側のステンシル設定は以下のようになります。
テストを有効にする
ステンシルテストが当然ながら必要となりますので、有効にします。

// ステンシルテスト設定 => 有効
g_pD3DDevice->SetRenderState(D3DRS_STENCILENABLE, TRUE);

ステンシルバッファへ反映する参照値設定
マスクの値としてステンシルバッファへ反映する参照値を指定します。

// ステンシルバッファへ反映する参照値設定 => 0x01
g_pD3DDevice->SetRenderState(D3DRS_STENCILREF, 0x01);

参照値マスクの設定
テストを行う際のステンシルバッファの参照値に対するマスクの値を指定します。
何も行わない場合は0xff等、ビットが削られない設定を行います。

// マスク設定 => 0xff(全て真)
g_pD3DDevice->SetRenderState(D3DRS_STENCILMASK, 0xff);

ステンシルテストの判定方法設定
ステンシルテストの合否判定のための判定方法を決めます。
以下のコードで使用している「D3DCMP_ALWAYS」は必ず成功する設定です。


// ステンシルテスト判定設定 => 必ず成功する
g_pD3DDevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS);

結果の組み合わせと反映内容
最後にステンシル、Zテストの結果の組み合わせと、その場合の反映方法を設定します。
組み合わせ定数の種類
定数 内容
D3DRS_STENCILFAIL ステンシル、Zテストともに失敗
D3DRS_STENCILZFAIL ステンシル成功、Zテスト失敗
D3DRS_STENCILPASS ステンシル、Zテストともに成功
以下のコードで使用している「D3DSTENCILOP_KEEP」は何もしない、 「D3DSTENCILOP_REPLACE」はピクセル側の参照値で置き換える設定です。 // ステンシルテストのテスト設定 g_pD3DDevice->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP ); g_pD3DDevice->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_REPLACE); g_pD3DDevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP );

オブジェクト描画

設定が完了したら、その後は通常の描画を行います。
この描画されるオブジェクトの内容がステンシルテストの対象となり、
バッファに書き込まれます。

注意

ここでは描画を行わないマスク設定について書いていますが、
実際には描画されているオブジェクトがマスクの役割を兼用することもあります。
サンプルのPractice02関数ではマップチップをマスクとして扱っています。

ステンシル設定

マスクが反映されたステンシルバッファの内容に影響を受けるためには
以下の設定を行います。

Zテスト設定

ステンシルのマスクでZテストを全て失敗設定にしているので、
Zテストを有効にした上でテスト方法を再指定します。


// Zバッファ設定 => 有効
g_pD3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
// ZBUFFER比較設定変更 => (参照値 <= バッファ値)
g_pD3DDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);

ステンシルテスト設定

ステンシルテスト側の設定はマスク側の設定とあまり変わりませんので
コードを先に紹介して異なる部分を解説します。


// ステンシルテスト => 有効
g_pD3DDevice->SetRenderState(D3DRS_STENCILENABLE, TRUE);

// ステンシルテストと比較する参照値設定 => 0x01
g_pD3DDevice->SetRenderState(D3DRS_STENCILREF, 0x01);

// ステンシルテストの値に対してのマスク設定 => 0xff(全て真)
g_pD3DDevice->SetRenderState(D3DRS_STENCILMASK, 0xff);

// ステンシルテストの比較方法設定 => 
//		この描画での参照値 == ステンシルバッファの参照値なら合格
g_pD3DDevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL );
		
// ステンシルテストの結果に対しての反映設定
g_pD3DDevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE);
g_pD3DDevice->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP);
g_pD3DDevice->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP);


設定の値が異なっているのは「比較方法」と「反映の設定」の箇所です。
比較方法
ステンシル側の比較方法は「D3DCMP_EQUAL」を使用しています。
これはピクセル側の参照値とステンシルバッファの参照値の値が
同じならば成功となる設定です。
※今回の設定でD3DCMP_EQUALを使用しているのでピクセル側の参照値は1にしていますが、
 別の反映方法の場合は、参照値は変化することが多いです。
反映の設定
反映の設定はステンシル、Zテストともに成功した場合のみ、ピクセルの参照値を
ステンシルバッファに反映することを許可しています。

オブジェクト描画

設定が完了したら、その後は通常の描画を行います。
この描画されるオブジェクトの内容がステンシルテストの対象となり、
バッファに書き込まれます。