1. はじめに:なぜ「見えないもの」を気にするのか
3Dゲームで、大きな建物や地形の裏に大量の敵やオブジェクトが隠れているシーンを想像してみてください。
プレイヤーの視点からは壁しか見えていないにもかかわらず、GPUは裏側にあるオブジェクトの頂点処理やピクセル描画を実行してしまうことがあります。
このような無駄な描画は**オーバードロー(Overdraw)**と呼ばれ、パフォーマンス低下の大きな原因になります。
そこで重要になるのが、
「完全に隠れているなら、そもそも描画しない」
という考え方です。
この最適化手法が、**Occlusion Culling(遮蔽カリング)**です。
2. 従来手法の限界とWebGPUの強み
従来はCPU側で「このオブジェクトは見えているか?」を判定し、描画対象を絞り込む方法が一般的でした。
しかし、オブジェクト数が数万規模になると:
- CPUの判定処理がボトルネックになる
- CPU → GPU 転送コストが増大する
といった問題が発生します。
ここで登場するのがWebGPUのCompute Shaderです。
可視判定から描画リスト生成までを、すべてGPU内部で完結させる
これにより、大量オブジェクトでもスケーラブルに処理できるようになります。
3. Two-Pass Occlusion Cullingの仕組み
「Two-Pass(2パス)」という名前の通り、処理は2段階で行います。
① パス1:深度情報を再利用する(前フレームの“記憶”)
まず、「何が手前にあるか」を知る必要があります。
そこで使うのが:
- 前フレームで描画した深度情報(Depth)
- それを粗くしたモザイク状の距離マップ
この縮小された深度テクスチャは一般にHi-Zバッファと呼ばれますが、本質は:
「ざっくりした遮蔽物マップ」
です。
② Compute Shaderで可視判定(カリング)
次に、描画対象となるすべてのオブジェクトに対して、GPU上で一斉に判定を行います。
判定内容はシンプルです:
- オブジェクトを囲む箱(バウンディングボリューム)を計算
- その領域に対応する深度マップを参照
- 「壁より手前か?奥か?」を比較
結果:
- 手前 or 一部でもはみ出す → 可視(描画対象)
- 完全に奥 → 不可視(破棄)
③ パス2:見えるものだけ描画
Compute Shaderが生成した「可視オブジェクトのリスト」を使い、最終描画を行います。
このとき重要なのが:
- 不可視オブジェクトは描画コマンド自体が発行されない
- GPUリソースを見えているものに集中できる
という点です。
4. Compute Shader実装イメージ(WGSL)
以下はコアロジックの簡略版です:
@group(0) @binding(0) var hi_z_texture: texture_depth_2d;
@group(0) @binding(1) var<storage, read> instances: array<InstanceData>;
@group(0) @binding(2) var<storage, read_write> draw_commands: array<DrawCommand>;
@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) id: vec3<u32>) {
let instance_index = id.x;
let instance = instances[instance_index];
// 画面上の範囲と最小深度を取得
let bounds = calculate_screen_bounds(instance);
let my_depth = bounds.min_z;
// 遮蔽側(壁)の深度を取得
let wall_depth = get_max_depth_from_hiz(bounds.uv_min, bounds.uv_max);
// 可視判定
if (my_depth <= wall_depth) {
// 描画対象に追加
atomicAdd(&draw_commands[0].instance_count, 1u);
}
}
ポイントは:
- 全オブジェクトをGPUで並列処理
- atomic操作で描画リストを構築
- CPUが一切介入しない
5. デバッグと可視化のコツ
この手法で一番難しいのは、
「正しく消えているか?」の検証
です。
実装時は以下のような仕組みを用意すると効率が上がります:
■ パラメータ調整UI
- カリングON/OFF
- 深度の許容値(バイアス)
- LOD切り替え距離
→ スライダーでリアルタイム調整
■ デバッグ視点
- カメラとは別に「俯瞰視点」を用意
- 壁の裏側を直接確認
→ 回り込んだ瞬間にオブジェクトが消えるかチェック
■ 可視化
- カリングされたオブジェクトを色分け
- Hi-Zマップを画面に表示
6. まとめ
Two-Pass Occlusion Cullingは、
「見えているものにだけ計算資源を使う」
という思想をGPUレベルで徹底する手法です。
WebGPUによって:
- 可視判定
- 描画リスト生成
- 描画実行
のすべてをGPU内で完結できるようになり、従来よりもはるかに効率的なレンダリングが可能になりました。
おわりに
「見えないものを計算しない」という発想は、シンプルながら非常に強力です。
大規模なシーンや大量オブジェクトを扱う場合、この考え方だけでパフォーマンスが劇的に改善することも珍しくありません。
WebGPU時代の最適化テクニックとして、ぜひ一度試してみてください。