W-Buffering

98/06/25(木)  − W-Buffering − DirectX6の新概念の謎

 DirectX6には、 清3Zさんのページでも紹介されているように、 さまざまな新機能が採用されている。 この中新機能の1つに、 W-Buffering(W バッファリング) というものがある。 DirectX5までのZバッファでは、 単純にZ座標をそのまま整数(の範囲に)に 変換してZテストしていた。 Wバッファとは、このテスト用のZ座標を 1 / Z で計算するというものである。

 何故こうするのか、理由は簡単である。 こうした方が Zテストの精度が向上するからだ。 ディスプレイ座標に変換された頂点座標は、 遠くにあるもの程小さくなっている。 従来のDirect3Dの計算方法では、 遠くにあってほとんど大きさを持たないような座標にも、 手前の方にある大きな座標にも同じZ座標の精度を持たせている。 このため、 遠くは不必要に精度が高く、視点近くでは精度が足らないという状況に陥っていた。 しかし、このWバッファの計算方法では、Zテストに使用するZ座標の精度は、 ディスプレイ座標のサイズに比例する ため、このような事態を防げるのである。
 Wバッファの採用には、もう1つメリットがある。 それは、Zテストに使用するZ値の計算誤差を軽減できるはずだからである。 こちらは意外と話に出ないので、簡単に説明しよう。
 グラフィックスハードウェアは、与えられた頂点を結び、ポリゴンを描画する。 与えられる座標にはZ値も含まれており、 これでZテストを行い、隠面消去を実行する。 このZテストは当然ピクセル単位で行う必要があるため、 ハードウェアは、与えられた頂点座標から、 ピクセル単位でz値を計算する必要がある。
 通常このZ値は、線形補間で求めているはずである。 ところが、もう気付かれた方もいらっしゃると思うが、 これでは正確なZ値は求まらないのである。
 ディスプレイ座標の求め方は、簡単に書くなら

dx = x / z
dy = y / z

である。 ただし、dx, dy がディスプレイ座標、x, y, z が視点座表系での頂点座標だ。 従来のZバッファでは、さらに

dz = z

となる。 x, y 座標をzで割っているのは、まあ 「2倍の距離にあるものは1/2の大きさに見える」 と単純に考えてもらえば解りやすいだろう。
実はこの式は、 三次元的に見たときポリゴンが曲面になる可能性がある。 まあ当然と言えば当然で、
dx = x / z
などとzで割ったものは、z値によって 反比例のグラフを描くような 座標系になるからである。
 ところが、zの計算にWバッファの計算式を使うと、つまり、

dx = x / z
dy = y / z
dz = 1 / z

で計算すると、座標系の直線が保持できるのである。
 では、曲面だと何が問題になるのだろうか?
 それが実は先程述べた、 ピクセル単位のz値を頂点座標からの線形補間で求められない ということなのである。 曲面の座標を線形補間で正しく計算する事ができないのは自明である。 パースペクティブコレクションのように、 ピクセル単位での透視変換が必要になってしまう。

 ところで、Zテストを行うのは ハードウェアだが、頂点のZ座標をハードウェアに 与えるのはソフトウェアである。 何が言いたいのかというと、 Direct3DのIM(イミディエイトモード)を使い、 1/z を元に計算した値をディスプレイ座標のz値として渡してやれば、 別にDirectX5以前 でも、このような処理は可能だということである。
 また、 OpenGLでは、 初めから1/zを元にする仕様となっている。 何故いまさら新機能 としてWバッファなどと言い出したのか、 これまで納得いかなかった。 もう1つ、Wバッファには、 ハードウェアの対応が必要ということもである。 上にも書いたように、ソフトウェア側の計算方法の見直しだけで済むはずなのに、 「なんでやねーん」 などと思っていたのだが、最近清3Zさんに、 その謎の答えを頂いた。
 分かってしまえば別になんと言う程のことでもなく、実は、 DirectX6のWバッファ というのは、Zテストそのものを、 浮動小数点で行うのだそうだ。 確かに、浮動小数点を使うというのは今までになかったから 「新」だし、 当然ハードウェアの対応が必要である。

 しかし、DirectX5まででは、Zバッファというのは、 かなりいい加減な計算方法だったことになる。 よくこれで不具合が出なかったものだと、妙に感心してしまった。