Share
Embed
【CEDEC2025】『鉄拳8』Unreal Engine 5 Niagaraによる開発事例と最適化 ~60FPSを守る~
「鉄拳8」VFX開発におけるNiagaraモジュールの実装事例と
膨大なアセットを扱いながら60FPSを維持するためのNiagaraの最適化事例について紹介します。
Niagaraを使って実現可能な表現や、モジュールを拡張実装した具体的な実装例も紹介します。
その中で、60FPSを下回らないためにNiagaraで行った最適化についても紹介します。
※本セッションはUnrealEngine5.2 を利用した事例になります。
Bandai Namco Studios Inc. PRO
December 22, 2025
Transcript
55 slides
-
『鉄拳8』Unreal Engine 5 Niagaraによる開発事例と最適化 ~60FPS を守る~ 株式会社バンダイナムコスタジオ テックスタジオ第1グループ制作1部プログラム1課 木下 領太
TEKKEN 8 & ©Bandai Namco Entertainment Inc. -
よろしくお願いします。 株式会社バンダイナムコスタジオ / エンジニア 木下 領太(きのした りょうた) 1999年生まれ。北海道紋別市出身。 2019年に株式会社バンダイナムコスタジオに新卒入社。 「鉄拳8」開発チームで主にNiagaraを利用した
リアルタイムVFXのテクニカルサポートや最適化を従事。 ◆鉄拳8で主な使用キャラ アリサ&ザフィーナ(最高段位は鉄拳王) 自己紹介 https://mombetsu.jp -
リアルタイム破壊表現を物理破壊プラグインを使わずNiagaraで表現した方法 イマイチ… プラグインの検証 APEX Chaos 5.0EA 4.27 4.26 β版 非推奨
5.0~ 不安定? APEX Chaos その他 ? 当初の開発環境は、Unreal Engine 4.27~5.0(EA)の間で、リアルタイム破壊をどのプラグインを利用するか 検討していました。 どれかを選定する必要がありましたが、検証の結果どちらも将来的に不安定であることが判明しました。 ・Apex Destruction →UE5.0以降はサポート対象外 ・Chaos Destruction →β版で負荷が高く、さまざまなアセットの置き換えが必要 -
リアルタイム破壊表現を物理破壊プラグインを使わずNiagaraで表現した方法 HoudiniのFractureノードを使用して、事前にモデルを分割し、破片1つをそれぞれのフレームへ原点配置します。 その後、改造したFluidのVATノードを利用して、各種データを書き出します。 - U: 各種頂点の情報 - V: 破片のIDとして利用(通常はアニメーションフレーム) Niagara
ROP より、HCSV(hjson)に各破片ごとに次の情報が書き出されています。 - 破片ID / 位置 / 質量 / コリジョン形状 など 破壊Niagaraのデータ構造 分割 原点に集める 破片の中心座標とID 破片をアニメーションフレームに割り振り (ID+座標)+(コリジョン形状+質量)をマージして破片ごとに設定 頂点情報 破 片 ID UV 破 片 ID 断面UVマスク 破 片 ID -
リアルタイム破壊表現を物理破壊プラグインを使わずNiagaraで表現した方法 Houdini Niagaraプラグインを有効化後、 HCSVをインポートするとHoudini Point Cache Assetとして読み込みが可能になります。 各種アセットをインポートした後、Fluid VAT用のマテリアルを構築してモジュールでHSVの読み込みを行います。 その後、Dynamic
Material Parameterを使用して、 UVのVにIDを渡すことで、破片IDごとに異なるモデルの形成が行われます。 Niagaraとの連携 破 片 ID HSVから位置を読み込む ParticleのIDをVとして割り振り -
リアルタイム破壊表現を物理破壊プラグインを使わずNiagaraで表現した方法 GENMAJI TEMPLEで落ち葉の数を5倍にした場合 ※実際のゲームとは異なります 挙動を利用した応用編 Niagaraで完結できるようにキャラクターとのインタラクションの仕組みを作成できるため、応用が可能です。 Niagara Parameter Collectionをモジュールに接続するだけで、VATやHCSVは必要ありません。 破片はPoint
Forceで動かしていましたが、落ち葉に関しては、追加でカールノイズと上方向への加速度の補正をし ています。 -
Niagaraモジュールの実装事例紹介 • ゲームスピードに応じたパーティクルの形状変化 • ゲームスピードに応じたパーティクルの制御 • 遮蔽可能なレンズフレアパーティクル • パーティクル空間の切り替えを、Niagara側で切り替える手法 •
番外編(Niagaraのシェーダー改造) • メッシュ空間の1軸を固定するメッシュビルボード表現 -
ゲームスピードに応じたパーティクルの形状変化 モジュール紹介 Delta Time (ゲームスピード)を任意のスケールで0~1に変換し、Lerpで各種パラメータに渡しています。 Floatの値がDynamic Inputの出力されるだけなので、スプライトサイズやVisibility Tagによるレンダラーの変 更など様々な箇所で利用可能です。 このようにNiagaraは任意の値を渡すことによって多様な表現が可能です。
RenderのVisibilityを変更する場合 スプライトサイズを変更する場合 60FPS固定なので1/60で0~1に変換 -
ゲームスピードに応じたパーティクルの制御 Delta TimeをInputに出して変更できるように改造 Particle側でDelta Timeを利用しているモジュールにも適応 モジュール紹介 Niagara Parameter Collectionに設定したゲームスピード(0.0~1.0)を使って2パターン出力できるよう設計してい ます。
1. NiagaraのDelta Timeの変動値をゲームスピードで相殺し、スローを無視した通常速度のDelta Timeを生成。 2. NiagaraのDelta Timeの変動値をゲームスピードで相殺し、任意の範囲でClampした速度のDelta Timeを生成。 -
メッシュ空間の1軸を固定するメッシュビルボード表現 NiagaraMeshRendererProperties.h enum class ENiagaraMeshLockedAxisSpace ~~ Local_Mesh_X, // X軸固定 Local_Mesh_Y,
// Y軸固定 Local_Mesh_Z, // Z軸固定 NiagaraMeshParticleUtils.ush #define PARTICLE_SPACE_MESH_LOCAL_X 3 ~~ NiagaraMeshParticleSRTParams Params.LockedAxisSpace if (Params.LockedAxisSpace == PARTICLE_SPACE_MESH_LOCAL_X)など Niagaraのエンジン改造部分の紹介をします。EnumとShaderの構造体の関係性です。 エンジンコードはDefineの値とEnumの値を比較しているので、以下のコードを拡張しています。 実装紹介 -
メッシュ空間の1軸を固定するメッシュビルボード表現 NiagaraMeshParticleUtils.ush float3x3 NiagaraGetMeshFacingMatrix(FLWCVector3 ParticleSimPosition, NiagaraMeshParticleSRTParams Params, float4 rotation) Const
float4 rot = normalize(rotation); ~~ 式省略 ~~ 以下3つはParams.LockedAxisSpaceで分岐する LockedAxis = float3((1.0f - (yy + zz)), (xy + wz), (xz - wy)); // X軸固定 LockedAxis = float3((xy - wz), (1.0f - (xx + zz)), (yz + wx)); // Y軸固定 LockedAxis = float3((xz + wy), (yz - wx), (1.0f - (xx + yy))); // Z軸固定 ~~ LockedAxis = NiagaraWorldToLocalVec(LockedAxis, Params);// 通常 NiagaraMeshParticleSRT NiagaraCalculateMeshParticleSRT(NiagaraMeshParticleSRTParams Params) if (Params.LockedAxisSpace == PARTICLE_SPACE_MESH_LOCAL_X ~~) SRT.Rotation = mul(FacingMat, NiagaraQuatTo3x3(Params.MeshRotation));// メッシュ軸固定の場合 else SRT.Rotation = mul(SRT.Rotation, FacingMat); // 通常 Enumで追加された分岐とメッシュの回転情報はShaderで計算しています。 LockedAxisでシステムの回転情報を渡している箇所の条件を分岐させてメッシュの回転情報を渡すように変更しました。 Rotation→Vectorに変換するモジュールと同じ計算 実装紹介 -
同じエフェクトを対戦に影響なく大量に発生させるための最適化 負荷がかかりやすい状況 NiagaraComponent生成 Emitter生成 Particle生成 Emitter生成 Particle生成 Spawnを呼ぶたびに同じ構成のNiagaraが新規で生成されるため、ComponentやEmitterの初期化に負荷がかかります。 さらに、Emitter数も増加するため、CPU負荷が非常に高まり、処理落ちやヒッチが発生しやすくなります。 x
呼び出し数分増える Call Count IncAvg IncMax ExcMax ExcAvg Stat NiagaraSystems -
同じエフェクトを対戦に影響なく大量に発生させるための最適化 初期化と持続負荷の軽減 AutoDestroyで消える前に同じインスタンスが呼ばれた場合、位置を更新して再度アクティブ化させています。 これにより、Emitterの数を1つに抑えられ、初期化コストや持続的な負荷を大幅に軽減できます。 Call Count IncAvg IncMax ExcMax ExcAvg
Stat NiagaraSystems 初回のみComponent類を生成 SetActiveでRespawn -
実際に導入しているNiagaraの最適化手法 PSOCacheのみだとヒッチに対する効果が少々不足していたため、PSOPreCacheを併用して実装しています。 PSOCache Niagaraのみ発生するレベルを作成してPSOを収集しています。その後、PSOを変換してパッケージに含め、 起動時にコンパイルを実行しています。 PSOPreCache UPrimitiveComponent を対象に、PostLoad の実行中にPSOを事前にキャッシュします。 UE5.3以降は更に機能が向上しています。
PSOCache r.PSOPrecache.ProxyCreationWhenPSOReady →描画がかなり遅延するのでオフにしています。 fx.Niagara.Emitter.ComputePSOPrecacheMode →Emitterが完了するまで描画を待ちます。動作優先 ▪PSOPreCache実装例 参考:The Matrix Awakens: An Unreal Engine 5 Experience WindowsEngine.ini r.PSOPrecaching=1 r.PSOPrecache.Resources=1 r.SkipDrawOnPSOPreCaching=0 r.PSOPrecache.ProxyCreationWhenPSOReady=0 r.pso.PrecompileThreadPoolPercentOfHardwareThreads=7 5 r.PSOPrecache.GlobalComputeShaders=1 r.ShaderPipelineCache.BatchSize=35 r.ShaderPipelineCache.BatchTime=14.0f fx.Niagara.Emitter.ComputePSOPrecacheMode=3 Niagaraを発生させてPSOCacheを収集するレベル -
その他Niagaraの最適化項目 半透明パーティクルの負荷の影響について 半透明はエフェクトの最適化項目として必ず通らなければなりません。 よくある負荷原因に加えて、Niagaraならでは可能な最適化手法を紹介します。 どうして… Base Pass Lights Translucency Post
Process Scene Frame Shader 持続時間 10.0ms 24.6ms ! ! ! -
>半透明パーティクルの負荷の影響について Distance Cullingでの最適化 鉄拳8では、画面のチラつきを防ぐため、基本的にカメラが近距離の場合はMaterial側でAlphaフェードを行っています。 しかし、半透明の描画は常に行われているため、画面に見えていない状況でもGPUに負荷がかかります。 そこで、NiagaraのRenderer側で一定距離のパーティクルを完全に描画非表示にすることで、半透明のGPU負荷を大幅に 軽減しています。 Alphaフェードのみだと無駄な負荷がかかる Distance Cullingで完全に描画削除
ストーリーモードのGENMAJI TEMPLEステージ デフォルトは無効化状態で 最小距離と最大距離を設定することができます。 = -
半透明パーティクルの負荷の影響について メッシュとマテリアルの最適化 Mesh Rendererで利用するStatic MeshはLODが使用できないため、大量のパーティクルを生成するとGPU負荷が上 昇しやすいです。 また、Two Sidedを設定したMaterialは、頂点数が実質的に2倍になるため取り扱いの注意が必要です。 基本的には、頂点数やパーティクル数を減らし、不必要なパーティクルの重なりを軽減して最適化します。 TwoSide:On
Alpha:0.05 TwoSide:Of f Alpha:0.1 薄い半透明メッシュが多数描画されることで処理負荷が高くなる例。TwoSideがオンになっており、追加で枚数やAlpha濃度を調整することで解決 -
その他Niagaraの最適化項目 最適化においての確認環境の重要性 キャラクターやステージの切り替え、モーション再生など、開発初期では負荷確認に多くの時間とコストがかかって いました。 そこで、事前に一定の最適化とレギュレーションを適応できるよう、エフェクトの単体確認環境を用意しています。 エフェクト実装 ROM確認 アセット確認 最適化FIX 開発初期
負荷計測 最適化 レギュレーション確認 NG OK アセットを検索・確認 エディタでゲーム確認 VFX班へ修正依頼 GPUProfilerで計測 アセットと原因の特定 エフェクト実装 最適化FIX 確認環境実装後 ROM確認 負荷計測 アセット単体確認 最適化 レギュレーション確認 NG OK GPUProfilerで計測しな くても、基準値内かどう かある程度判別可能に! アセットをツールで確認 VFX班へ修正依頼 COST