Rendering & implementation

Indirect Drawing

インダイレクト描画

GPUバッファの引数から描画数を決める。

3 / 10

Interactive demo

Preparing the demo…

Indirect Drawing

Drag to orbit, Ctrl/⌘ + scroll to zoom. Use two fingers on touch screens.

This explanation is currently available in Japanese.

ひとことで

描画命令の引数(何個描くか、どこから描くか)を、CPU ではなく GPU のバッファから読み込んで描く方法です。GPU のコンピュートシェーダーで「見える物体」を選び出すと、その数は GPU のメモリの中にあります。CPU がそれを読み戻してから命令を出すと、GPU の処理が終わるのを待つか、数フレーム古い結果を使うことになります。インダイレクト描画では、GPU が引数バッファに数を書き込み、描画命令はそのバッファを参照するだけなので、待ちも遅れもありません。

ゲームでの使いどころ

  • GPU カリング:数万〜数百万の物体の視錐台・遮蔽カリングを GPU で行い、残った物体だけを描く(GPU ドリブンレンダリング)。
  • GPU の粒子:生きている粒子の数が GPU の中で変わるので、その数だけ描く。
  • 大量の異なるメッシュ:多数の描画命令の引数を 1 本のバッファに並べ、1 回の命令でまとめて実行する(マルチドローインダイレクト)。
  • Nanite のような仕組み:細かく分けたメッシュのかたまりを GPU が選び、描く数を自分で決める。

しくみ

インデックス付きの描画の引数は、5 つの整数の組です。

値意味
indexCount1 個の形のインデックスの数
instanceCount描く個数
firstIndexインデックスの読み始める位置
baseVertex頂点番号に足す値
firstInstanceインスタンスの番号の始まり

流れ:

  1. コンピュートシェーダーで、各物体が見えるかを判定します。
  2. 見えた物体の番号を、プレフィックスサムやアトミックな加算で、詰めた配列に書き込み、同時に instanceCount を数えます。
  3. drawIndexedIndirect(argumentBuffer) のように、引数バッファを指定して描画命令を出します。CPU は描く数を知る必要がありません。

CPU と GPU は並行して動いているので、GPU の結果を CPU が読むには、GPU の仕事の完了を待つ必要があります。待つと CPU が止まり、待たなければ数フレーム前の結果を使うことになります。インダイレクト描画は、この「読み戻し」をなくすための仕組みです。

実装のポイント

  • 毎フレーム数を 0 に戻す:カリングの前に、instanceCount を 0 に戻す処理(クリア)を忘れないようにします。
  • 描く物体の番号の配列:頂点シェーダーは、visibleIds[instance_index] から本当の物体の番号を引き、その行列を読みます。
  • WebGL には無い:WebGL にはインダイレクト描画の命令がありません。WebGPU や、DirectX 12・Vulkan・Metal で使えます。
  • デバッグ:引数バッファの中身は CPU から見えないので、開発中は読み戻して表示できる仕組みを用意すると、問題を見つけやすくなります。

つまずきやすい点

  • 引数の並びや型の間違い:5 つの値の順番や、32 ビット整数であることを取り違えると、何も描かれないか、おかしな物が描かれます。
  • CPU で数を使いたくなる:「何個描いたか」をゲームの処理で使いたくなることがありますが、読み戻すと利点が消えます。必要なら数フレーム遅れの値で済ませます。
  • 小さな規模では効果が薄い:物体が数百個程度なら、CPU でカリングしても十分です。インダイレクト描画は、物体が非常に多い場合に効きます。

デモで試すこと

  1. 上から見た地図の中心にゲームのカメラがあり、青い扇形の中の物体(緑)だけを描きます。右下が、描画命令の引数バッファです。
  2. 「CPU が数を読み戻してから描く」では、数フレーム前のカリングの結果で描くので、カメラを回すと扇形の先の方で描き漏れ(赤)が出て、後ろではもう見えない物をむだに描きます。
  3. 「読み戻しの遅れ」を大きくするほど、描き漏れが増えます。「カメラを回す速さ」を上げても増えます。
  4. 「インダイレクト描画」に切り替えると、GPU がそのフレームのカリング結果で instanceCount を書くので、描き漏れもむだもなくなります(このデモは WebGL で流れを再現したものです)。

References

Unity ComputeShader ↗

A general reference for this subcategory.