Zバッファの使い方

・基礎シリーズ第四弾

 なんか、α合成、混合要素、αテストに続いて機能の説明と使い方が続いてますね。エフェクトな話ももっとしないと。後で連続でやってみたいなぁ。

 Zバッファの使い方、と言っても初期化の方法などは書きません。ここはそういうページじゃないので、マニュアルやヘルプを見てください。SDKのD3DFrame.cppなんて参考になるかと。

 なお、PowerVR系ではできるかどうかわからない話ばっかりです。どうなんでしょう?

 

・半透明が無ければ完璧なZバッファ法

 まずZバッファ法の説明。ポリゴンを描画する時一緒にZバッファと呼ばれるメモリにもZ値(視点からの距離)を書き込みます。そして新たにポリゴン描画する時に、Zバッファを使って前書いたポリゴンと今書こうとしているポリゴンのZ値をピクセル(ドット)毎に比較(Zテスト)して、描画しようとするポリゴンのZ値の方が小さい(前書いたポリゴンのピクセルより手前に来ている)なら描画し、大きい(前のポリゴンより奥)なら描画しないという手法です。
この説明だとちょっとわかりにくいですね。

 この方法を使えば、ポリゴンにめり込んだポリゴンとかもできます。それに、どんな描画順でも結果は変わらないので描画順を気にする必要も無いです。

 欠点は、半透明の物体が苦手な事。描画順は関係ないと書きましたが、半透明となると話は別です。Zバッファを使うと、描画順によっては、半透明で向こうが透けて見えるはずの物まで見えなくなります。半透明の場合は、Zソートで奥から手前の順番に描画されるようにすると正しく描画できます。この時Zバッファへの書き込みはしないように。まぁZソートしなくてもそれほど変になる訳では無いです。
加算合成
なら奥だろうと手前だろうと描画順番は関係ありません。

 今回はZバッファ法ではなくZバッファの方に注目してみます。

 


ティーポットを描画

 


その時のZバッファの内容。
Z値は一番手前が0.0、奥が1.0なので
画像にすると奥ほど白く、手前ほど黒い

    
わかりやすく、色を反転して
コントラストを上げてみた。
色を反転したので奥ほど黒い

 ちなみにDirect3DはZ値を小数で扱いますが、実際にZバッファに書き込まれている内容は整数です。Wバッファを使うと小数ですが。

・Zテスト各種

 Direct3DのZテストは、何も”Dest(ZバッファのZ値) >= Src(これから書こうとするポリゴンのZ値)の時に描画”に固定されてる訳ではありません。αテスト同様、いろいろな比較方法を選択できます。

D3DCMPFUNC列挙型

D3DCMP_NEVER

D3DCMP_LESS

D3DCMP_EQUAL

D3DCMP_LESSEQUAL

D3DCMP_GREATER

D3DCMP_NOTEQUAL

D3DCMP_GREATEREQUAL

D3DCMP_ALWAYS

常に失敗

Dest > Src の時成功

Dest == Src の時成功

Dest >= Src の時成功

Dest < Src の時成功

Dest != Src の時成功

Dest <= Src の時成功

常に成功

 なんか、どこかで見た表ですね。って前回見たばっかりですが。前回のαテストの時と同じ、D3DCMPFUNC列挙型を使います。設定方法も前回説明したαテストと同様です。

IDirect3DDevice3::SetRenderState( D3DRENDERSTATE_ZFUNC, D3DCMPFUNC );

というわけで今回も、以下D3DRENDERSTATE_D3DCMP_を略します、便宜上。

 デフォルト値はLESSEQUALで、Srcの方が手前もしくは同じ時だけ描画するようにしています。LESSじゃなくてLESSEQUALなのは、マルチパステクスチャブレンディング(描画したポリゴンの上に同じ大きさ・形・位置で別の色・テクスチャのポリゴンを重ねて描画する手法)のためでしょう。マルチパステクスチャについてはこの次書くつもりの「マルチなテクスチャ」でちゃんと説明するつもりです。マルチプルテクスチャとの違いとかも。……また機能の説明と使い方ですね。

 話が逸れました。
さて、果たしてZテストの比較関数を変える意味はあるんでしょうか?GREATERを使ったらポリゴンの奥にめり込んだポリゴンしか書かなくなるし、EQUALなんて一体何に使えるのか……。比較関数を変えたところで実用性があるのか?

 

・影とEQUAL

 正直言って、LESSEQUALで大体事足ります、普通の使い方をする限りは。


普通と違う使い方。
一部をワイヤーフレーム化

 

 EQUALの使い方としては、ほとんどは何か書いた後に重ねて書くのに使うでしょう。じゃないと描画されないし。やっぱりマルチテクスチャのような使い方が一番多いでしょうね。LESSEQUALではなくEQUALを使わなければいけない時というのも少ないですが。

 これはEQUALじゃなきゃいけないという使い方としては、ポリゴンに影や光が落ちる時ですね。例として、次のような物に影を落とす時はどうすればいいでしょうか?

 


上に白い格子、
下に煉瓦の床。
白い格子はカラーキー

 


それを上から見る。
白い格子だけに
影を落としたい

 
理想

 LESSEQUALのままだとこうなります。


俺がやりたいのは
こんなのじゃない!

 ここでEQUALの出番です。白い格子と同じ高さで描画すると、理想の画像になります。
……と、言いたいところですが、ここで精度の問題が出てきます。


EQUALを使用。
見るも無残

 これは以下のような問題に性質が似てます。


まず線を引く

 


その線の2点を
選び直線を引く

 
新たな線が前の線と
重なった所は白、
重ならなかった所は青

 見ての通り、前に書いた線と同じ位置で書いたつもりでも、必ずしもなぞって書ける訳ではありません。
ということで、残念ながらEQUALは精度の問題であまり役に立ちません。「じゃあどうすんだよ」っていうことになりますが、代わりにGREATEREQUALを使いましょう。影を書く時位置を白い格子よりちょっとだけ下にすると、煉瓦の床と白い格子の間だけ描画します。これでちゃんと白い格子に影が落ちます。


Z値が大きい時は下、
小さい時(手前)は上の図。
白が格子、茶色が床のZ値

 


灰色は影のZ値。
GREATEREQUALによって
白より下の部分だけが描画

 

・Zテスト以外にも使おう

 Direct3Dで使うZバッファのサーフェイスは、普通のサーフェイスと同じでBltやLockなどが使えます(無理な3Dボードもありそうですが)。最初の方でZバッファの内容を画像にした絵を使ってますが、あれもLockを使ってます。

 それを利用して、バイオハザードやFF7のような一枚絵の背景もできます。あれらは数回に分けて描画してるようですけど(プレステはハードがZバッファに対応してないので)。
一度時間のかかるような背景の描画をして、その画像とZバッファを予め作っておいた退避用サーフェイスにBltします。そして、次からはClearの代わりにその退避用サーフェイスの内容をBltするだけ。

 視点から目的のポリゴンまでに他のポリゴンが無いかを調べたり、ある点の視点から一番手前の物体まではどれくらいの距離があるか、なんてのもZバッファを調べることで簡単に可能です。後者は距離の計算が簡単じゃないですが。
例えば、光の位置周辺のZ値を調べる事で、グローの大きさを変えたりグローを消したりできます。


この赤い点の分だけZ値を調べ、
手前に何かあったらその分だけ
グローを小さくする
(画像は使いまわしです)

 この手法は、1080°スノーボーディング(N64)のCMで動いてる画面を見た時、太陽にこんな方法が使われていたように見えました。実際には見た事無いですが、Unrealもこの方法使ってるんじゃないでしょうか。グロー出しっぱなしというわけにもいかないと思うので。

 

・つけっぱなしにしてないで切りなさい

 ついでに。Zバッファ法は、ピクセル毎にZテストをします。故にZバッファを使わない時に比べてちょっと遅いです。特に、背景や全画面など広い領域を描画する時は描画するピクセルが多い分遅くなります。Zテストをする必要が無い場合でもZテストをすると、それだけ無駄が増えます。

 という事で、Zテストが必要無い時は使わないように設定しましょう。例えば、空などの背景を描画する時は大体ZテストもZバッファへの書き込みも必要無いでしょう。広い領域を描画するので、それなりに違いが出てきます。
背景→物体→半透明物体という順に描画するなら、背景はZテスト・Zバッファ書き込みなし、不透明の物体はZテスト・Zバッファ書き込みあり、半透明の物体はZテストあり・Zバッファ書き込みなしという感じでしょうか。この後文字などの情報をZテスト・Zバッファ書き込みなしで書く、と。

 ちなみにこれはα合成やスペキュラーの使用など、他の機能の設定にも言える事です。必要無い時は使用しないのが一番です。あまりに切り替えが多いと、その切り替えのオーバーヘッドの方が問題になる場合もありますが。SetRenderStateはそれなりに時間食いますから。

 

written by Y.Ohde  e-mail : oode@alles.or.jp

back