ステンシル

■ステンシル

ステンシルピクセルごとに値を設定し、その値に基づいてピクセルの
描画を行うかどうかは判断する描画手法のことです。
このピクセル単位の値を保存する領域をステンシルバッファ、
ステンシルバッファへ設定するための値を参照値と呼びます。

gmpg_0118

●ステンシルバッファ
	ステンシルバッファはフレームバッファと同じ大きさが用意されており、
	各ピクセルに対して割り当てられているサイズは1バイトです。
	つまりステンシルバッファを使用する場合256段階の設定が可能ということになります。
	DirectX9ではZバッファの領域の一部としてバッファが用意されています。

	gmpg_0119

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

	・ステンシルテストで使用する情報
		ピクセル側に設定された参照値:
			フレームバッファに描画するためのピクセルに設定された参照値

		ステンシルバッファの参照値:
			ステンシルバッファに描き込まれている参照値

		ステンシルマスク:
			ステンシルバッファの参照値に対して有効な値を決めるための値

		判定方法:
			ステンシルテストの合否判定方法

			gmpg_0120

		描き込みの組み合わせ:
			ステンシルバッファへの描き込むかどうかの判断は
			ステンシルテストの結果とZバッファテストの結果の
			組み合わせで決めることできる

			gmpg_0121

			これはどれか1つではなく、3つ全てに設定することが可能

		描き込む内容:
			ステンシルバッファへの描き込む新しい参照値

			gmpg_0122

	以下はステンシルテストの処理イメージです。
	
	gmpg_0123


●ステンシルバッファへの描き込み方法(DirectX9版)
	ステンシルバッファに参照値を描き込むにはいくつかの設定が必要です。

	・ステンシルバッファをクリアする
		ステンシルバッファを使用する場合Clear関数で
		ステンシルバッファの初期化を行う必要があります。
			
		例:
			Clear(0L,
				NULL,
				D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL,
				D3DCOLOR_ARGB(255, 0, 0, 0),
				1.0f,	// Zバッファの初期値
				0// ステンシルバッファの初期値
				);		

	・Zバッファテストを有効にする
		ステンシルにはZバッファテストが必要となりますので、
		Zバッファを有効にします。

		例:
			SetRenderState(D3DRS_ZENABLE, TRUE);

	・Zバッファテストを必ず失敗
		ステンシルバッファへ描き込み際は基本的にフレームバッファへの
		描き込みは行わないので、Zバッファテストを必ず失敗させて
		フレームバッファへの描き込まないようにします。

		例:
			SetRenderState(D3DRS_ZFUNC, D3DCMP_NEVER);

	・ステンシルテストを有効にする
		ステンシルテストを有効にします。

		例:
			SetRenderState(D3DRS_STENCILENABLE, TRUE);


	・ステンシルバッファへ描き込む参照値
		ステンシルバッファへ描き込む参照値を決めます。
		0~255まで設定が可能です。

		例:
			SetRenderState(D3DRS_STENCILREF, 0x02);


	・ステンシルマスク
		ステンシルバッファの参照値に対するマスクの値を決めます。

		例:
			SetRenderState(D3DRS_STENCILMASK, 0xff);

	・ステンシルテストの判定方法
		ステンシルテストの合否判定のための判定方法を決めます。
			
		例:
			SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS);

	・合否の組み合わせと描き込み内容
		ステンシルテストとZテストの合否の組み合わせによる

		例:
			SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP);
			SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_REPLACE);
			SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP);

■サンプルソース

●使用リソース
	サンプル使用画像

●ソース
	#include <windows.h>
	#include <d3d9.h>
	#include <d3dx9.h>

	// 静的ライブラリ
	#pragma comment(lib, "d3d9.lib")
	#pragma comment(lib, "d3dx9.lib")

	// 構造体宣言
	typedef struct _VERTEX
	{
		D3DXVECTOR3 pos;
		D3DCOLOR color;
		float tu;
		float tv;
	}VERTEX;

	// グローバル変数
	LPDIRECT3D9 g_pD3DInterface;	// DirectXインターフェース
	D3DPRESENT_PARAMETERS *g_pD3DPresentParam;
	LPDIRECT3DDEVICE9 g_pD3DDevice;
	LPDIRECT3DTEXTURE9 g_pTexture = NULL;

	// プロトタイプ関数宣言
	BOOL MainLoop(void);
	void StencilDraw1(void);
	void StencilDraw2(void);
	void PolygonDraw(void);

	LRESULT CALLBACK WindowProc(HWND window_handle, 
					UINT message_id, 
					WPARAM wparam,
					LPARAM lparam)
	{
		switch (message_id)
		{
		case WM_CLOSE:
			PostQuitMessage(0);
			break;
		default:
			return DefWindowProc(window_handle, message_id, wparam, lparam);
			break;
		}

		return 0;
	}

	int APIENTRY WinMain(HINSTANCE hInstance,
		HINSTANCE hPrevInstance,
		LPSTR     lpCmpLine,
		INT       nCmdShow)
	{
		HWND hWnd;
		WNDCLASSEX window_class = {
			sizeof(WNDCLASSEX),		// 構造体のサイズ
			CS_HREDRAW | CS_VREDRAW,	// クラスのスタイル
			WindowProc,			// ウィンドウプロシージャ
			0,				// 補助メモリ
			0,				// 補助メモリ
			hInstance,			// このプログラムのインスタンスハンドル
			LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION)),// アイコン画像
			LoadCursor(NULL, IDC_ARROW),	// カーソル画像
			NULL,				// 背景ブラシ(背景色)
			NULL,				// メニュー名
			TEXT("StencilTest"),		// クラス名
			NULL				// 小さいアイコン
			};

		// 構造体の登録
		if (RegisterClassEx(&window_class) == 0)
		{
			return false;
		}

		// ウィンドウ作成
		hWnd = CreateWindow(
			TEXT("StencilTest"),
			TEXT("StencilTest"),
			(WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME) | WS_VISIBLE,
			400,
			10,
			400,
			400,
			NULL,
			NULL,
			hInstance,
			NULL);

		if (hWnd == NULL)
		{
			return false;
		}

		// インターフェース作成
		g_pD3DInterface = Direct3DCreate9(D3D_SDK_VERSION);
		if (g_pD3DInterface == NULL)
		{
			// 作成失敗
			return false;
		}

		g_pD3DPresentParam = new D3DPRESENT_PARAMETERS;
		if (g_pD3DPresentParam == NULL)
		{
			return false;
		}
		ZeroMemory(g_pD3DPresentParam, sizeof(D3DPRESENT_PARAMETERS));

		// バックバッファの数 => 1
		g_pD3DPresentParam->BackBufferCount = 1;
		// バックバッファのフォーマット => D3DFMT_UNKNOWN(フォーマットを知りません)
		g_pD3DPresentParam->BackBufferFormat = D3DFMT_UNKNOWN;
		// ウィンドウモード設定 => 定数で切り替え
		g_pD3DPresentParam->Windowed = true;

		// スワップエフェクト設定 => ディスプレイドライバ依存
		// スワップエフェクト => バックバッファとフロントバッファへの切り替え方法
		g_pD3DPresentParam->SwapEffect = D3DSWAPEFFECT_DISCARD;

		// 深度バッファの有無
		g_pD3DPresentParam->EnableAutoDepthStencil = TRUE;

		// 深度バッファのフォーマット
		g_pD3DPresentParam->AutoDepthStencilFormat = D3DFMT_D24S8;

		// 多様なフラグ設定
		g_pD3DPresentParam->Flags = 0;

		// DirectDeviceの作成
		if (FAILED(g_pD3DInterface->CreateDevice(D3DADAPTER_DEFAULT,
			D3DDEVTYPE_HAL,
			hWnd,
			D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED,
			g_pD3DPresentParam,
			&g_pD3DDevice)))
		{
			return false;
		}

		// ビューポートパラメータ
		D3DVIEWPORT9 view_port;

		// ビューポートの左上座標
		view_port.X = 0;
		view_port.Y = 0;
		// ビューポートの幅
		view_port.Width = g_pD3DPresentParam->BackBufferWidth;
		// ビューポートの高さ
		view_port.Height = g_pD3DPresentParam->BackBufferHeight;
		// ビューポート深度設定
		view_port.MinZ = 0.0f;
		view_port.MaxZ = 1.0f;

		// ビューポート設定
		if (FAILED(g_pD3DDevice->SetViewport(&view_port)))
		{
			return false;
		}

		//テクスチャーを作成。
		D3DXCreateTextureFromFileEx(g_pD3DDevice,
			TEXT("Stencil.png"),
			D3DX_DEFAULT,
			D3DX_DEFAULT,
			0,
			0,
			D3DFMT_UNKNOWN,
			D3DPOOL_MANAGED,
			D3DX_DEFAULT,
			D3DX_DEFAULT,
			0,
			NULL,
			NULL,
			&g_pTexture);


		ShowWindow(hWnd, SW_SHOW);
		UpdateWindow(hWnd);

		while (true)
		{
			bool message_ret = false;
			MSG msg;

			if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
			{
				if (msg.message == WM_QUIT)
				{
					break;
				} else {
					TranslateMessage(&msg);
					DispatchMessage(&msg);
				}
			} else {
				MainLoop();
			}
		}
	}

	BOOL MainLoop()
	{
		g_pD3DDevice->Clear(0L,
			NULL,
			D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL,
			D3DCOLOR_ARGB(255, 0, 0, 0),
			1.0f,		// Zバッファの初期値
			0);		// ステンシルバッファの初期値

		g_pD3DDevice->BeginScene();

		//ライティングを無効にする。
		g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);

		D3DXMATRIX matProj, matView, matWorld;

		//射影座標変換
		D3DXMatrixPerspectiveFovLH(&matProj,
			D3DX_PI / 4.0f,
			4.0f / 3.0f,
			0.1f,
			500.0f);
		g_pD3DDevice->SetTransform(D3DTS_PROJECTION, &matProj);

		//ビュー座標変換
		D3DXMatrixIdentity(&matView);
		g_pD3DDevice->SetTransform(D3DTS_VIEW, &matView);

		//ワールド座標変換
		D3DXMatrixIdentity(&matWorld);
		g_pD3DDevice->SetTransform(D3DTS_WORLD, &matWorld);

		g_pD3DDevice->SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1);

		StencilDraw1();
		StencilDraw2();
		PolygonDraw();

		g_pD3DDevice->EndScene();

		g_pD3DDevice->Present(NULL, NULL, NULL, NULL);

		return TRUE;
	}

	void StencilDraw1(void)
	{
		// Zバッファ設定 => 有効
		g_pD3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE);

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

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

		// ステンシルバッファへ描き込む参照値設定 => 0x02
		g_pD3DDevice->SetRenderState(D3DRS_STENCILREF, 0x02);

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

		// ステンシルテスト比較設定 => 必ず成功する
		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);

		// ステンシルバッファには三角形を描画
		VERTEX v[3];

		v[0].tu = 0.0f;	v[0].tv = 0.0f;
		v[1].tu = 1.0f;	v[1].tv = 0.0f;
		v[2].tu = 0.0f;	v[2].tv = 1.0f;

		v[0].color = D3DCOLOR_ARGB(255, 255, 255, 255);
		v[1].color = D3DCOLOR_ARGB(255, 255, 255, 255);
		v[2].color = D3DCOLOR_ARGB(255, 255, 255, 255);
		v[2].color = D3DCOLOR_ARGB(255, 255, 255, 255);

		v[0].pos.x = 0.0f; v[0].pos.y = 3.5f; v[0].pos.z = 29.0f;
		v[1].pos.x = 6.0f; v[1].pos.y = -2.5f; v[1].pos.z = 29.0f;
		v[2].pos.x = -6.0f; v[2].pos.y = -2.5f; v[2].pos.z = 29.0f;

		g_pD3DDevice->SetTexture(0, NULL);

		g_pD3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 1, v, sizeof(VERTEX));

	}

	void StencilDraw2(void)
	{
		// Zバッファ設定 => 有効
		g_pD3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE);

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

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

		// ステンシルバッファへ描き込む参照値設定 => 2
		g_pD3DDevice->SetRenderState(D3DRS_STENCILREF, 0x02);

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

		// ステンシルテスト比較設定 => 必ず成功する
		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);

		// ステンシルバッファ(逆三角形)
		VERTEX v[3];

		v[0].tu = 0.0f;	v[0].tv = 0.0f;
		v[1].tu = 1.0f;	v[1].tv = 0.0f;
		v[2].tu = 0.0f;	v[2].tv = 1.0f;

		v[0].color = D3DCOLOR_ARGB(255, 255, 255, 255);
		v[1].color = D3DCOLOR_ARGB(255, 255, 255, 255);
		v[2].color = D3DCOLOR_ARGB(255, 255, 255, 255);

		v[0].pos.x = -6.0f; v[0].pos.y = 2.0f; v[0].pos.z = 29.0f;
		v[1].pos.x = 6.0f; v[1].pos.y = 2.0f; v[1].pos.z = 29.0f;
		v[2].pos.x = 0.0f; v[2].pos.y = -4.0f; v[2].pos.z = 29.0f;

		g_pD3DDevice->SetTexture(0, NULL);

		g_pD3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 1, v, sizeof(VERTEX));
	}


	void PolygonDraw(void)
	{
		// ステンシルバッファ => 有効
		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_LESS);

		// ステンシルテストの結果に対しての反映設定
		g_pD3DDevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP);
		g_pD3DDevice->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP);

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

		VERTEX v[4];

		v[0].tu = 0.0f;	v[0].tv = 0.0f;
		v[1].tu = 1.0f;	v[1].tv = 0.0f;
		v[2].tu = 0.0f;	v[2].tv = 1.0f;
		v[3].tu = 1.0f;	v[3].tv = 1.0f;

		v[0].color = D3DCOLOR_ARGB(255, 255, 255, 255);
		v[1].color = D3DCOLOR_ARGB(255, 255, 255, 255);
		v[2].color = D3DCOLOR_ARGB(255, 255, 255, 255);
		v[3].color = D3DCOLOR_ARGB(255, 255, 255, 255);

		v[0].pos.x = -35.0f; v[0].pos.y = 35.0f; v[0].pos.z = 100.0f;
		v[1].pos.x = 35.0f; v[1].pos.y = 35.0f; v[1].pos.z = 100.0f;
		v[2].pos.x = -35.0f; v[2].pos.y = -35.0f; v[2].pos.z = 100.0f;
		v[3].pos.x = 35.0f; v[3].pos.y = -35.0f; v[3].pos.z = 100.0f;

		g_pD3DDevice->SetTexture(0, g_pTexture);

		g_pD3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, v, sizeof(VERTEX));
	}