69K Views
October 25, 22
スライド概要
近年ではモバイル端末でもCompute ShaderやDraw Indirectが利用可能になりました。これらの技術を用いて、モバイル上で大量のオブジェクトを描画するテクニックをご紹介します。
GPU上でカリングとLOD判定を行い、GPUインスタンシングによって描画を行うことで、CPUとGPU間のデータの転送を最小限に抑え、大量のオブジェクトに対して効率的な描画を行うことができます。これらについて具体的なコードとサンプルを用いながら丁寧に解説を行います。
こんな人におすすめ:
・モバイルでも大量のオブジェクトを描画したい方
・Unityの高度なグラフィックス機能を使いたい方
・ゲームのパフォーマンスチューニングに関心のある方
受講者が得られる知見:
・ComputeShaderやDrawIndirectなどの高度なグラフィックス機能
・GPU上でカリングやLOD判定を行う実装方法
・GPGPUやGPU駆動レンダリング
出演:
細田 翔 (KLab株式会社)
--
初出: SYNC 2022 #UnitySYNC
https://events.unity3d.jp/sync/
リアルタイム3Dコンテンツを制作・運用するための世界的にリードするプラットフォームである「Unity」の日本国内における販売、サポート、コミュニティ活動、研究開発、教育支援を行っています。ゲーム開発者からアーティスト、建築家、自動車デザイナー、映画製作者など、さまざまなクリエイターがUnityを使い想像力を発揮しています。
モバイル向け 大量描画テクニック KLab株式会社 細田 翔
KLab株式会社 画像 技術統括部 グラフィックスエンジニア / テクニカルアーティスト 細田 翔 シェーダなどのプログラミングを利用した映像制作が趣味。国内外のデモパーティで優勝歴あり。 KLab株式会社でグラフィックスエンジニア/テクニカルアーティストとしてUnity製モバイルゲームの3D グラフィックス開発を担当。 効率的なレンダリングパイプラインの設計から実装、モバイル向けに最適化 したシェーダの開発、アプリの負荷の全自動プロファイリングシステムの開発を行う。 2
モバイル上でも大量描画したい! シーン上に数万個の大量のGameObjectを配置する方法 処理負荷的に非現実的😰😰 実装上の工夫でモバイル上で大量描画を実現😊😊 3
例 シーン全体で100万本の草 iPhone 11で60FPS 4
草の大量描画 ×1万 = 1インスタンス 100本の草 シーン全体で100万本の草 5
プロファイリング結果(iPhone 11) GPU処理をdispatchしているだけなので CPUの負荷はほとんどない👍👍 ● CPU負荷 ○ 描画システムのUpdate処理 0.19 ms まだまだGPUに余力がある👍👍 ● GPU負荷 ○ GPUの使用率 35% ○ GPU全体 5.8ms※ ○ GrassInstancing(描画用シェーダー) ■ 3.3 ms ○ Culling(GPUカリング+LOD判定のComputeShader) ■ 0.412 ms Unity Profiler ※iPhoneでは電力消費を抑えるためにGPUの処理性能が可変 最大性能を発揮すればGPU全体で2.03ms程度(5.8msにGPU使用率の0.35を乗算) Xcode 6
目次 ● 大量描画システムの概要 ● 実装方法の解説 ● 応用例 ● まとめ 7
目次 ● 大量描画システムの概要 ● 実装方法の解説 ● 応用例 ● まとめ 8
大量描画システムの概要 Unity上で大量のオブジェクトを効率的・高速に描画するシステム(社内ライブラリ) Unity2021 + URP12 特徴 ● ComputeShader + DrawIndirect によるGPU駆動レンダリング ○ GPUによるカリング ○ GPUによるLOD判定 ● GPUインスタンシングによる描画 ● 高い汎用性 ○ 大量描画の仕組みは描画用のシェーダーと疎結合 ○ 描画用のシェーダーを変えることで幅広い用途に応用可能 ■ 草の描画 ■ キャラクターアニメーション 9
GPU駆動レンダリングとは? GPU駆動 = 何を描画するのかをGPUが決める 10
GPU駆動レンダリングとは? ● 通常のレンダリング ○ CPUがレンダリングの主導権を握り CPUからGPUに描画の命令をする(管理型) ● GPU駆動レンダリング ○ GPUがレンダリングの主導権を握り 描画対象までGPUが判断・決定をする(自立型) 11
GPU駆動により効率的に描画できる理由 ● GPUの並列実行 ○ 大量のデータ(大量のオブジェクト)の処理に適している👍👍 ○ カリングやLOD判定をGPU処理にして高速化 ● CPU↔GPU 間のデータの転送を最小限に抑えることができる ○ 通常の描画方法:毎フレームCPU→GPUのデータ転送が必要👎👎 ○ 今回の描画方法:初期化時に1回だけCPU→GPUのデータ転送👍👍 GPU駆動レンダリングと大量描画の相性は抜群 12
目次 ● 大量描画システムの概要 ● 実装方法の解説 ● 応用例 ● まとめ 13
GPU駆動レンダリングに必要な機能 ● ComputeShader ○ GPUで汎用計算するためのシェーダー ○ GPGPU(GPUの演算資源を汎用計算に活用)に使われる ● DrawIndirect ○ ComputeShaderの結果を描画コマンドの引数にできる機能 ○ データをCPU経由せずにGPUだけで描画の制御ができる 14
対応端末 ● ComputeShader + DrawIndirectのシステム要件 ○ OpneGL ES3.1+/Vulkan ■ Android端末 世界シェアカバー率 71.15%(2020年8月30日) ○ Metal (Apple A9 iPhone 6s+) ■ iPhone端末 国内シェアカバー率 99% 配信ダッシュボード | Android デベロッパー ほとんどの端末で利用可能😆😆 15
GraphicsBufferとは? ● ComputeBufferを将来的に置き換えるUnityのクラス ○ ComputeBufferを汎用化・抽象化した上位互換 参考記事 ● Unity2020.1 以降では ComputeBuffer を使わず GraphicsBuffer を使おう by @fuqunaga 今回はGraphicsBufferを採用 16
レンダリング全体の流れ ● Initialize処理 ○ CPU側で配置情報からカリング前のGraphicsBufferを生成 ■ GraphicsBufferの中身(インスタンスデータ) ■ ■ ■ 姿勢行列(位置・回転・スケール) TintColor アニメーションのオフセット時間 CPU→GPUのデータ転送は初 期化時に1回しか行わない ● Update処理 ○ GPU側でカリングとLOD判定 ■ ComputeShaderでカリングとLOD判定を実行 ■ LODレベルごとにカリング後のGraphicsBufferを生成 ○ GPUインスタンシングで描画 ■ LODレベルごとに Graphics.RenderMeshIndirect を実行 17
GPUカリング ComputeShaderでカメラの視錐台とAABBの交差判定を実装 ● カメラの視錐台は6枚のPlaneから定義 →AABBとPlaneの交差判定から、視錐台の内外判定ができる 【画像出典】wikipedia 参考記事(C#からComputeShaderに移植) ● 視錐台とAABBとの交差判定 - e.blog by @edo_m18 カメラの視錐台の6枚のPlaneを取得するUnityビルドイン関数 ● GeometryUtility.CalculateFrustumPlanes カメラの視界に入っているかを判定 不要なオブジェクト描画をスキップ 18
カリング後のインスタンス数の取得 ● カリング後のGraphicsBufferのTarget指定 ○ GraphicsBuffer.Target.Structured | GraphicsBuffer.Target.Counter ○ enumをビットフラグとして組み合わせ ● カウンターの値を別のGraphicsBufferにコピーするメソッド ○ GraphicsBuffer.CopyCount ○ IndirectArguments用のGraphicsBufferにインスタンス数をコピー先にする GraphicsBufferだけで処理が完結! CPUはインスタンス数を把握せずに描画できる 19
LOD判定 LOD = Meshのポリゴン数を動的に切り替える機能 ● カメラからの距離に応じてLODレベルを切り替え ○ 近くのオブジェクト ハイポリで綺麗に描画 ○ 遠くのオブジェクト ローポリで負荷を削減 SceneView(俯瞰視点) LODレベルごとに色分け ●LOD0(ハイポリ) ●LOD1(中間) ●LOD2(ローポリ) GameView(カメラ視点) 20
LOD判定 LODレベルを切り替えるカメラからの距離 Mesh・マテリアルをインスペクターで指定 21
LOD判定の最適化 ComputeShaderの同じKernelで、カリングとLOD判定の両方を実装 1回のdispatchで、カリングとLOD判定を同時実行 22
目次 ● 大量描画システムの概要 ● 実装方法の解説 ● 応用例 ● まとめ 23
さまざまな応用例 ● 大量描画の仕組みは描画用のシェーダーと疎結合 ● 描画用のシェーダーを変えることで幅広い用途に応用可能 GPUインスタンシングに対応した通常のシェーダー 24
応用例① 草原 ● 頂点シェーダーで風による草の揺れを再現 ● 揺れによる変形に応じて法線を再計算 25
応用例② 任意形状への配置 ● テクスチャを元に配置情報を生成 26
配置データの生成 Houdini上でScatter → JSON出力 → ScriptableObjectに変換 JSON Scriptable Object Houdini上でテクスチャの画素値を densityとしてPointをScatter 27
応用例③ 任意形状のMesh ● 3万ポリゴンの複雑なMeshのインスタンシング描画(1万インスタンス) ● シャドウマップ対応 ○ Tags { "LightMode" = "ShadowCaster" } のPassを定義 28
応用例③ 任意形状のMesh シャドウキャストの有無は インスペクターで指定可能 29
応用例③ 任意形状のMesh Houdiniのリダクションにより4段階のLODモデルを用意 LOD0 30000ポリゴン LOD1 10000ポリゴン LOD2 2000ポリゴン LOD3 200ポリゴン 30
応用例④ VATとImposterのハイブリッド ● キャラクターアニメーション(ユニティちゃん) ● LODレベルによって描画手法を切り替え ○ 近距離 VAT(Vertex Animation Texture) ○ 遠距離 Imposter Billboard © Unity Technologies Japan/UCL 31
応用例④ VATとImposterのハイブリッド 3段階のLODを用意 LOD0 VAT 8191ポリゴン LOD1 VAT 2457ポリゴン LOD2 Imposter 32x32ピクセル 4ポリゴン © Unity Technologies Japan/UCL 32
大規模なシーンへの対応 オープンワールドのような大規模なシーンの場合 パフォーマンスが悪化😫😫 ● カメラから遠く離れたインスタンスもGPUカリングの計算が行われる ● 不適切なカリングの粒度(常に1インスタンスずつカリング) 33
大規模なシーンへの対応 大量描画 システム 大量描画 システム 大量描画 システム 大量描画 システム 大量描画システムという 大きな単位で CPUによってカリング GeometryUtility.TestPlanesAABB 大量描画 システム 1つの大量描画システム でシーンを構成 複数の大量描画システム でシーンを構成 CPUとGPUのカリングの2段階構成を実現 ● ● カメラから遠く離れた大量描画システムは CPUの1段階目のカリングで処理をスキップ グループ単位の適切な粒度のカリング 大規模なシーンに対応! 34
目次 ● 大量描画システムの概要 ● 実装方法の解説 ● 応用例 ● まとめ 35
まとめ ● 2022年現在、70%以上のモバイル端末でComputeShaderとDrawIndirectが利用可能 ○ カリングとLOD判定のGPU実装が可能 ○ CPUを経由せず、GPUで描画対象を管理 ○ 非常に効率的な大量描画が可能! ● 描画用のシェーダーを変えることで、さまざまな用途に応用できる ○ 風に揺れる草原 ○ VATやImposterを組み合わせたキャラクターアニメーション Unityでは高度なレンダリング機能を プラットフォームの違いをあまり気にせずに利用できる! 36