引き続きDepth Bounds Testについてです。APIの説明は後に回します。
Deferred shadingを行う時の、Depth Bounds Testの設定コストを考える
Depth Bounds Testは、どこにも明示されていませんが、レンダーステートの一種であることは容易に想像がつきます。この設定にかかるコストは、Depth functionやCull modeを変更するのに近いものだと思われます。したがって設定を変更すると、直前のプリミティブの描画が終了するまで、次のプリミティブの描画が開始されないような気がします。そのため、あまり頻繁に設定を変更すると、かえって遅くなってしまうのではないかという懸念が生じます。そこで考えられる方法は、シーン全体のローカルライトをいくつかのデプスの範囲で区切って、その範囲ごとにDepth Bounds Testを設定し、レンダリングを行うというものです。そうすれば、設定回数を少なくすることが出来るうえ、Depth Bound Testの効果も得られると考えられます。
百聞は一見にしかずということで、実際のDeferred shadingでローカルライトのaccumulationを行っているシーンを用いて測定してみました。
想定シーン
実際のゲームのレンダリング。総計184個のローカルライトが存在。分布は画面全体の広範囲にわたる。デプスバッファの複雑度は、FPSのインドアシーンに準ずる。使用GPUはGeFroceGTX 680。計測はシーン全体の1フレームあたりのレンダリング時間。(したがって他のレンダリングパスも含む)
結果
Depth Bound Test を使用しない場合 | 15.15ms |
シーン内のライトを5つのデプス範囲に区分し、範囲とにレンダリングを行う 各範囲内のライトの個数は次の通り(27,31,65,44,17) |
12.19ms |
レンダリングするライトごとにDepth Bound Testを設定してレンダリング | 11.36ms |
意外なことに、個々のライトごとに、184回のDepth Bounds Testの設定を行ったケースが一番早い結果になりました。つまりレンダーステートの変更コストよりも、ピクセルの早期カリングの方がメリットが大きいということだと思います。加えて、個々のライトに設定するケースでは、ライトをデプス値に応じてソートする必要がないので、CPU側の手間もほとんどありません。レンダリングの品質を一切妥協することなく、単純に導入することで、4ms近いレンダリング時間が短縮できました。ミドルレンジのGPUでこれを行えばさらに大きな短縮時間になると思います。
DirectX9におけるDepth Bounds Testの設定方法
Depth Bounds Testの設定はレンダーステートの一つである、D3DRS_ADAPTIVETESS_X,Z,Wに設定します。XにFourCCでNVDBと設定することで、Depth Bounds Testが有効になります。Yは未使用(要注意!)で、続くZ,Wに境界値となる最小値と最大値を設定します。Depth Bounds Testを解除する際は、D3DRS_ADAPTIVETESS_Xをクリアします。
//設定 dev->SetRenderState(D3DRS_ADAPTIVETESS_X,MAKEFOURCC('N','V','D','B')); dev->SetRenderState(D3DRS_ADAPTIVETESS_Z,*(DWORD*)&zMin); dev->SetRenderState(D3DRS_ADAPTIVETESS_W,*(DWORD*)&zMax); //解除 dev->SetRenderState(D3DRS_ADAPTIVETESS_X, 0);
デプスバッファには、シェーダーで加工しない限り、正規化デバイス座標系に変換された同次座標を正規化した際のZ値が書き込まれています。Depth Bounds Testで使用する境界値も、同様に変換して設定する必要があります。簡単な例では下記の通りです。この値は非常にセンシティブなので、x87命令による80bitレジスタなどによって生じる、GPU上の計算とCPU上の計算の誤差には注意が必要です。
D3DXVECTOR4 a(0.0f, 0.0f, boundary, 1.0f); D3DXVec4Transform(&a, &a, Proj); boundary = a.z / a.w;
DirectX10/11におけるDepth Bounds Testの設定方法
DX10/11には、ADAPTIVETESSというレンダーステートは存在しません。DX10/11でこの機能を使うためには、NVIDIAからリリースされているNDA版のNVAPIが必要です。
OpenGLにおけるDepth Bounds Testの設定方法
OpenGLでは、GL_EXT_depth_bounds_test というExtensionによってDepth Bounds Testがサポートされています。設定方法は、この拡張のサポートの有無を確認した後、下記APIで設定します。
void glDepthBoundsEXT(clampd zmin, clampd zmax);