W-Buffering 3rd

2000/07/13(木)  − W-Buffering 3rd − すいませんウソ書いてました(大汗

 98.08.02 のコラムで「謎は解けた?」とか 書いておきながら、実は大嘘を書いていました。 推測でモノを書くとロクなことになりませんね。 このコラムを読んでくださっている人も結構いらっしゃるようなので、 本来なら間違いが判った時にすぐ訂正すべきだったのですが、大変遅くなってしまいました。
ていうか訂正が遅れたのは単なる手抜きでしかありません。

大変申し訳ありません(大汗

 えぇと、そういう訳で気を取り直して結論から言おう。 W-Buffering とは、その名の通りデプステストにW値を利用する 方法である。 ここで言うW値とは、 視野座標系から透視変換を行った後のw であり、 Direct3D の場合は視野座標系でのzと等しい(仮に OpenGL で実装された場合は−zとなるだろう)。 ヘルプなどで透視変換行列を調べて確認してみよう。 また、以前 W-Buffering は浮動小数点で実装される という話も書いたが、 実際には 浮動小数点であるか否かは特に関係ない(別問題である)ようだ。
 さて、W-Buffering はw値(つまり透視変換前のz値)であるため、 ラスタライズ段階での座標系(透視変換後のスクリーン座標)では、 w値はx,y値に対して非線形に変化してしまうという性質がある。 スクリーン座標系でのz値による通常のデプステストでは、 x,y,z値がすべて透視変換された後の値であるため、 ポリゴン内部のz値は頂点からの単純な線形補間によって求まる。 しかし、w値を用いる場合、ピクセル単位での パース補正 が必要になるということを意味している。 このためリアルタイム処理では、 ハードウェアでの実装がほとんど前提となる。

 さらに注意すべき点として、 w値はrhw(wの逆数)から計算される ため、 自前で座標変換を行う場合は、 rhwに適切な値(1/w)をセットしておく必要がある。 さらに、 デプステストに使われるwの最大値(視点座標系でのファークリップ)は プロジェクションマトリクスから計算される ため、 自前で座標変換する場合もプロジェクションマトリクスをセットしておかなくてはならない。 実際に必要な要素は一部なので、
matProj._43 = 0;
matProj._34 = 1;
matProj._44 = dvWNear; // not used(現在の実装ではニアクリップ値は使われない)
matProj._33 = dvWNear / (dvWFar - dvWNear) + 1;
だけで良い(nVIDIA の W-Buffering に関する資料より抜粋)。

 さて、従来のようなz値によるデプステストに対し、 w値によるテストを行うメリットは何だろうか。 それは、 z値によるデプステストでは、Z-Buffer の精度を有効に活用することが難しい ことに他ならない。 もっとも 24 bits や 32 bits といった大きな深度を持つ場合あまり問題になることはない。 問題になるのは 16 bits 程度の精度の低い Z-Buffer を使う場合である。 当然だが透視変換を行った座標は、視点から離れるほどサイズが小さくなる。 ピクセルの分解能が低くなることは一目瞭然であろう。 そしてこれと同じことはz値にも起こり、 遠くの座標ほど Z-Buffer の分解能が低くなる。
 例えばニアクリップ面とファークリップ面との距離の比率が 100 倍 ある場合、 ニアクリップ付近のポリゴンとファークリップ付近のポリゴンでは 画面上のサイズに 100 倍程の差が現れ、 Z-Buffer の分解脳も同様に 100 倍程の差が現れる。 ニアからファーまで、16 bits なら 65536 段階のz座標しか存在しないのに、 ニアとファーの位置で 100 倍の精度差が現れるのは、 かなり痛い現象であることがなんとなく判るだろう。
 あくまで Z-Buffering でこの問題を回避(軽減)するためには、 なるべくニアクリップとファークリップの距離の比率を小さくするしかない。 具体的には、 ニアクリップをできる限り遠くに設定する ことだ。 距離の比率を小さくするにはこれが最も有効だからである。
 考えれば容易に判ることだが、 Z-Buffering ではニアクリップと ファークリップの距離の比率がほとんどそのまま精度に直結する。 例えば ニア 10.0、ファー 100.0 に設定(ニアとファーの距離の比率が 10)した際のzバッファ精度は、 ニア 0.01、ファー 100.0 に設定(比率 10000)した場合の精度と比較して 1000 倍以上にもなる。 何故なら、 ニアを 0.01 にセットしている場合、Z-Buffer の全分解能の内 99.91% が ニア(0.01)〜 10.0 までの間に割り当てられてしまう からだ。 逆に 10.0 〜ファー(100.0)の間には、全分解能の内 0.09% しか使えない。 16 bits Z-Buffer では、10.0 〜 100.0 の間には実に 59 段階程度 しか分解能が無いのである。 これでは表示が破綻して当然だ。 当たり前だが、ニアを 10.0 にセットしていれば、10.0 〜 100.0 の間に 65536 段階使える。 つまり、この違いだけで Z-Buffer 精度に 1111 倍 もの差が出るのである。
 しかし、ニアクリップ平面を遠くにするにも限界があるため、 場合によってはこの方法では対処しきれないことがある。

 このような場合に Z-Buffering の代わりに W-Buffering を利用する訳である。 W-Buffer であれば近くも遠くも精度が同じになる ことから、 特にニアクリップの距離を気にしなくとも、遠くの描画が破綻することが無いからである。