Zバッファ法(デプスバッファ)

■描画順番

複数のポリゴンを画面に出力する場合、ポリゴンの描画順番は
非常に大きな意味を持ちます。
	
	gmpg_0110	

上の図の位置関係のポリゴンを描画する場合、ポリゴンAとポリゴンBの順番を変えると
以下のように結果が異なります。

	gmpg_0111	

こちらの意図している結果は「ポリゴンB => A」の順番ですので、
カメラから離れているポリゴンから描画していくときちんと描画されます。
実際に「このカメラから離れているポリゴンの順番」通りに描画することを考慮すると
かなりの処理時間を割くことになります。
全てのモデルのポリゴン情報を考慮して描画する場合「処理コスト = 総ポリゴン数に比例」となります。
近年の数万ポリゴンのモデルが描画されているゲームにおいては
この処理コストは当然ながら歓迎されません。
そこで考えられたのがZバッファ法(デプスバッファ)という手法です。

■Zバッファ法(デプスバッファ)

Zバッファ法ピクセル単位に持たせている深度情報を利用した隠面消去の方法です。
深度情報を保存できるメモリ領域のことをZバッファデプスバッファ深度バッファと呼んでいます。
このZバッファには0.0から1.0の値を保存できるようになっており、
0が最前面1が最背面として扱われています。

●Zバッファの流れ
	①.Zバッファ情報のクリア
		Zバッファの情報はフレームバッファ(バックバッファ)の情報と同様に
		基本的には毎フレーム初期化を行います。
		前のフレームの深度情報が今回のフレームに影響されないようにするためです。

	②.ポリゴンを描画
		ポリゴンをフレームバッファに描画する際はピクセル単位で描き込みます。
		その書き込むためのピクセル一つ一つに対して深度情報が設定されており、
		1つのピクセルを描き込むためには自分の深度とZバッファの深度の確認を行います。
		もし、自分の深度がZバッファの深度よりも小さければ(手前にあれば)
		Zバッファを更新し、フレームバッファに描き込みを行い、
		深度よりも大きければ(奥にあれば)、描き込みは行いません。

		・例1:
			描き込むピクセルの深度 0.3
			Zバッファの深度 0.4

			結果:
				ピクセルの描き込み => OK
				Zバッファの深度 => 更新有り(0.4 => 0.3)
				
			理由:
				描き込むピクセルの深度がZバッファの深度よりも小さいため
				手前に描画されると判断しピクセルの描画と
				Zバッファの更新が行われる

		・例2:
			描き込むピクセルの深度 0.9
			Zバッファの深度	0.2

			結果:
				ピクセルの描き込み => NG
				Zバッファの深度 => 更新なし

			理由:
				描き込むピクセルの深度がZバッファの深度よりも大きいため
				奥に描画されると判断し、ピクセル描画と
				Zバッファの更新は行わない

●デプステスト
	デプステストとは上の「●Zバッファの流れ -②.ポリゴンを描画-」の項目にある
	描き込む予定のピクセルの深度とZバッファの深度を比較してピクセルを書き込むかを
	判定するためのテストのことです。
	デプステストはポリゴンごとではなくピクセル単位で判定しているので、
	ポリゴン同士が重なったとしてもきちんと前後判定がされます。

	gmpg_0112	

●メリット
	描画順番を意識せずに描画処理を行える

●デメリット
	深度情報用のバッファを用意する必要あるので、メモリの使用量が増える

●その他
	Zバッファ法の使用は任意ですので、必要かどうかの判断は開発側でします。
	3Dモデルの描画には必要だと思いますが、プレイヤーの体力や、スコアなどの
	UIには必ずしも必須というわけではありません。
	ですのでプログラム上でZバッファ法使用の有無の設定を
	簡単に行えるようにした方がいいと思います。

■Zバッファとアルファブレンドの相性問題

Zバッファとアルファブレンドはあまり相性がよくなく、
何も対策を立てずに描画を行ってしまうとこちらの意図した結果とは違う内容になってしまいます。

●問題点
	アルファブレンドによるピクセルの算出方法には「フレームバッファのピクセルのRGB値」
	「新規ピクセルのRGB値」「アルファ値」を使用します。
	この計算で使用される「フレームバッファのピクセルのRGB値」がZバッファを使用した際に
	問題となります。
	Zバッファではカメラの奥から順番に描き込む必要はないので、
	アルファ値を持ったピクセルを描き込む時点のフレームバッファのピクセルは
	計算するために必要なピクセルが描きこまれていない可能性があります。

	前提:
		赤ポリゴン Z 0.5 (アルファ値 1.0f)
		緑ポリゴン Z 0.2 (アルファ値 0.5f)

	正常な描画結果:
		gmpg_0115	

	Zバッファを使用(赤ポリゴン => 緑ポリゴンの順番):
		gmpg_0116	


	Zバッファを使用(緑ポリゴン => 赤ポリゴンの順番):
		gmpg_0117	

●対策
	対策は透過ポリゴンと不透過ポリゴンが描画処理で混在している場合、
	不透過ポリゴンを先に描画し、その後透過ポリゴンを描画することで
	ある程度改善されます。
	ただ、透過ポリゴンでも描画順番を気にする必要があります。
	ポリゴン単位は難しいのでオブジェクト座標によるソートをかけるだけでも
	かなりの効果を発揮します。

■DirectX9での使用方法

Zバッファ法はDirectX9で簡単使用することができます。

●DirectX9でのZバッファ法の流れ
	①.初期設定
		DirectX9のデバイス設定時のD3DPRESENT_PARAMETERS構造体で
		このプログラム上でのZバッファ法の使用の有無を決めます。

		設定パラメータ:
			EnableAutoDepthStencil
				深度バッファの使用の有無です。
				TRUEなら使用、FALSEなら未使用となります。

			AutoDepthStencilFormat
				深度バッファのフォーマットを指定します。

	②.バッファのクリア
		バッファのクリアはIDirect3DDevice9のClear関数で行います。
		Clear関数の第三引数に「D3DCLEAR_ZBUFFER 」を設定します。
		この時にフレームバッファのクリアも同時に行う必要がありますので
		「D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER」とします。

	③.描画時の設定
		Zバッファの使用の有無はSetRenderStateで行います。
		第一引数のパラメータは「D3DRS_ZENABLE」に設定し、
		第二引数はZバッファを使用するなら「TRUE」未使用なら「FALSE」にします。

		使用:
			SetRenderState( D3DRS_ZENABLE , TRUE );
		
		未使用:
			SetRenderState( D3DRS_ZENABLE , FALSE );

●Zテストの比較方法
	DirectXのZテストの深度比較はいくつかの方法が用意されています。
	デフォルトの設定は以下の内容です。
		
		「現状のバッファ深度 >= 新規のバッファ深度だった場合ピクセルを更新する」

	・比較方法の変更
		深度の比較方法の変更はSetRenderStateで変更可能です。

		書式:
			SetRenderState(D3DRS_ZFUNC, 比較設定);

		具体例:
			SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);

	・変更例
		以下はD3DCMP_LESSEQUALとD3DCMP_LESSの描画結果です。

		前提条件:
			1.ポリゴンA(赤三角形) => ポリゴンB(緑三角形)の順番で
			  描画関数を実行する

			2.ポリゴンA、Bのカメラからの距離(z値)は同じ

		D3DCMP_LESSEQUAL(現状 >= 新規なら新規にピクセルを更新):
			gmpg_0165	

		D3DCMP_LESS(現状 > 新規なら新規にピクセルを更新):
			gmpg_0166	

		上の内容の通り、Z値が同じ状態で赤 => 緑の三角形を描画した場合、
		「D3DCMP_LESSEQUAL」では緑が赤の三角形の上に描画されていますが、
		「D3DCMP_LESS」では赤が緑の上に描画されています。
		これは「D3DCMP_LESS」の場合、「緑三角形のZ値 > 赤三角形のZ値」の条件が
		真にならないので、緑の三角形のピクセルが上書きされていないので、
		赤が緑の上に描画されているようになっています。