微妙に謎がある勾配命令 ddx, ddy(アセンブリ命令では dsx, dsy) について調べてみました。
これはGPUごとに動作の違いがあるかもしれないので私が調べたものがすべてのGPUに適用されるわけではないでしょうからお気を付けください。
ちなみに、うちのGPUはGeForce8600GTSです。安物ですね。
調査方法は4x4のレンダリングターゲットにポリゴン等を書いてみて、その過程をPIXで追うというだけ。
頂点のテクスチャ座標として適当な数列を与えています。わかりやすいものからランダムなものまで。
泥臭いけど確実な方法ですね。
まず、ポイントリストで16の点を各ピクセルに描画してみましたが、ddx, ddy 命令での出力は 0 でした。
やはりこの命令は1つのプリミティブ(点1つ、三角ポリゴン1つ)の間で有効な命令のようです。
では、ポリゴンを描画した場合どうなるか?
頂点に与えた数列をそのまま引数にすると隣接ピクセル(ddx なら右、ddy なら下)の同じ引数との差分が出力されました。
単純に隣接ピクセルとの間で差分をとるだけっぽいのかと思ったのですが、いろいろ計算してみるとそうでもないらしいことがわかります。
例えば2つの適当な数列で内積して2乗とかしてみると隣接ピクセルとの差になってない場合が出てきます。
よく調べてみると、どうやらいくつかのピクセルにおける勾配関数の戻り値は同一です。
GPUによって挙動は違うかもしれませんが、8600GTSでは同一の戻り値になるのは2x2の領域です。
つまり、2x2の領域において左上のピクセルと右上のピクセルの差分が ddx で、左上のピクセルと左下のピクセルの差分が ddy で取得できる形になるようです。
あるポリゴンの描画範囲が2x2の右下のみを描画する場合があったとしても左上を中心として計算しているようです。
GPUは2x2の領域の1ピクセルでも描画される状況であれば領域すべてのピクセルを計算するものと思われます。
この際、ddx や ddy が来たタイミングの同一レジスタの値の差分を返すというものなのでしょう。
『ShaderX5』には勾配関数を使用してタンジェントスペースなしでバンプマップをやる手段について記載されていますが、完全なピクセル単位というわけにはいかないようですね。
まあ、2x2なら誤差範囲と言っていいと思いますし、人間の目にはそれらの違いを理解することは難しいと思いますが。