Top > プログラミング関連 > Zバッファのウマい使い方 その1 - アルファブレンディングとの共存
2008/2/28 記述
Windows付属のペイントを使っても分かるように、グラフィックの描画では、基本的に一番最後に描画された物だけが表示され、それまであった物は上書きされて消えてしまう。3Dプログラムでも同様のことが起こり、背後にあるべきポリゴンが前面に描画されて奇妙なことになる。
背後にあって見えない面を描画しないことを隠面消去と呼ぶ。隠面消去のための手法がいくつか考案されたが、その中で一番よく使われていると思われるのがZバッファ法(デプスバッファとも呼ばれる)である。
Zバッファとは、ピクセルごとにZ座標(奥行き方向)を記録したバッファのことで、最前面に描画されたピクセルのZ値が記録されている。DirectXではZの値は最前面が0.0f、最遠方が1.0fになっている。
3Dオブジェクトを描画(レンダリング)するときに、描画済みの物とこれから描画する物とでピクセルごとにZ座標の値を比較することで前後関係を把握し、背後にあって見えないピクセルを描画しないで済むようになる。これにより、描画の効率化と、隠面消去が行える。
さて、単にポリゴンを描画したいだけなら、この機能は十分役立つ。ところが、半透明オブジェクトの描画を行おうとすれば、この機能が逆に邪魔になる。
半透明オブジェクトを描画する時、ピクセル単位で背景色と描画色の合成を行う。背景のオブジェクトが既に描画されており、最前面に半透明オブジェクトを描画するだけなら、この色の合成はうまく行く。
ところが、逆に半透明オブジェクトが前面に描画済みで、背後に不透明なオブジェクトを描画しようとしたときには、隠面消去が働き、背後の不透明オブジェクトは描画されない。
実例を図1に示した。正常な画像は図2。半透明の壁の背後に存在する人体モデルが描画されていない。また、壁を突き抜けている物体が途中で描画されなくなっている。
このあたりの問題は「DirectX によるゲームプログラミング入門 : 第 3 章 DirectX Graphics の特殊効果」に簡単な説明があるが、それによればZソートを行ってから描画することが解決策だとされている。手順は以下の通り。
要は、後ろにある物から描画しろ、ということだ。
ここでZソートという単語が出てきた。これは隠面消去の簡単な方法の一つで、アイデアとしては面ごとに奥行き座標を示すZ値を計算し、奥にある面から描画していくというものである。面同士が交差している場合などに正しい結果が得られないのであまり使われていない。詳しい説明は「【CG教科書】Zソート法」を参照して欲しい。
ただ、上に書いてあるとおりの手順を実装して、面ごとに処理するのはいささか面倒である。今作成しているプログラムでは、もっと簡単に視点からオブジェクトの重心座標までの距離を奥行きと見なして実装した。
その結果が図2である。簡易的な実装だが、十分に機能している。今回はわざわざ載せるほどのコードもないが、一応Zソート部分のコードを載せておく。
//Zソートに使う構造体
typedef struct{
float fDist; //視点から重心までの距離
IGameRenderZ *lpRender; //描画対象オブジェクトへのポインタ
}ZSORTINFO,*LPZSORTINFO;
//Zソート対象のオブジェクトの配列
typedef std::vector<LPZSORTINFO> ZARRAY;
struct ZSORTFUNC{ //比較用関数オブジェクト
bool operator()(const LPZSORTINFO &a, const LPZSORTINFO &b) const{
return a->fDist > b->fDist; //大→小にソート;遠い方から近い方へ
}
};
bool ZSort(ZARRAY &zArray) //Zソート
{
if(zArray.empty())return false;
ZSORTFUNC zs;
std::sort(zArray.begin(),zArray.end(),zs);
return true;
}
このコードにはNYSL 0.9982を適用します。