原点から離れた座標で計算精度が原因と思われるノイズが発生する問題について

お世話になっております。

表題の件について、質問があります。

原点から大きく離れた場所でレベルシーケンスを再生した際、Niagaraで実装されたVFXに微小な振動が発生することがありました。

このレベルシーケンスを、原点に移動させて再生したところ、振動が解消されました。

これは、浮動小数の計算精度不足によるノイズが原因ではないか、と予想しています。

私が経験した事例では、原点から離れた状況で、精度が必要なフェイシャルのスキニング計算に同様のノイズが発生したことがあります。

Epic Developer Assistantの助言を元に、Transform Origin機能を試してみたのですが、ノイズは解消されませんでした。

UE5で、この「浮動小数の計算精度不足」を解消するには、どのような選択肢がありますでしょうか。

[Attachment Removed]

お世話になっております。

お問い合わせありがとうございます。

振動が生じたVFXはMeshタイプでしたでしょうか?

取り急ぎですが、一点、お試しいただきたいことがあります。

Unreal Engine には、描画コールを組み立てる直前まで内部計算を64ビット精度で行っていたとしても、描画コール時にTransformデータを圧縮してメモリを節約する仕組みがあり、デフォルトで有効化されております。これは(特に)回転の精度に悪影響を与え、原点から遠く離れた地点での回転したオブジェクトの位置のズレを生じさせることがあります。

このTransform圧縮を実施しないよう設定するには、エンジンのConfigフォルダにあるDataDrivenPlatformInfo.ini(たとえば、リリース版エンジンのWindows版であれば、“C:\Program Files\Epic Games\UE_5.3\Engine\Config\Windows\DataDrivenPlatformInfo.ini”)をテキストエディタで開き、bSupportsSceneDataCompressedTransforms=trueと記述されている箇所をすべて、bSupportsSceneDataCompressedTransforms=falseとなるように書き換え、エンジンを再起動します。

(※大元のシェーダの静的分岐を変更するため、再起動の際に大規模なシェーダーの再コンパイルが行われます)

このTransform圧縮の問題点が、さまざまな描画を差し置いて、Niagaraやフェイシャルでいきなり顕在化するかと問われると、必ずしも確証を持てないというのが正直なところですが、広域マップのゲームで精度問題をよく解決してきたフラグですので、切り分けを兼ねて一度お試しいただければと思います。

以上、取り急ぎご返信申し上げます。

[Attachment Removed]

迅速なご返答ありがとうございます。

上記情報を担当者に伝えて原因切り分けのための検証を行ってみます。

[Attachment Removed]

> 振動が生じたVFXはMeshタイプでしたでしょうか?

該当のVFXはSpriteとLightの複合タイプとなっています。有効なエミッターが6つあって、スプライト4つとライト2つでした。

(Meshタイプのエミッターもありますが、Disableとなっています。)

また、Engine\Config\Windows\DataDrivenPlatformInfo.ini にて

bSupportsSceneDataCompressedTransforms を全部falseに書き換えた後にエディターを起動して

LevelSequenceActor経由でシーケンサーを開いてみましたが改善はされませんでした。

以上、​よろしくお願いいたします。

[Attachment Removed]

お世話になっております。

ご検証ありがとうございました。

ご報告いただいた件は、ひょっとしますと、こちらの

「Niagara Sprite and Ribbons are shaking when very far from world origin」

[Content removed]

と同じ症状である可能性があります。その場合、純粋に精度限界ということになりますが、もう少し調査したいと思います。

まず、できれば再現をとってみたいと思いますので、以下の情報をご提供いただけますでしょうか。

・「Fountain」のようなNiagaraテンプレートでも再現するか

・問題の起きているNiagaraシステムが配置されている絶対のワールド座標

・Niagaraシステムを直にレベルに配置してもジッタリングが生じるか否か

・レベルシーケンス内でのアクタ位置アニメーションの有無(シーケンス内でNiagaraアクタの位置を動かしているか否か)

・再現環境はWindows Editorのみか、それともStandaloneでも発生するか

また、原因調査のヒントを得るために、以下の点もお試しいただけますと助かります。

・GPU←→GPUシミュレーションの切り替え

ジッタリングが起きているNiagaraエミッタのシミュレーションモードを切り替えたときに振動に変化がみられるか

・再生速度の切り替え

コンソールコマンド「slomo」を用いて、半速(slomo 0.5)、2倍速(slomo 2.0)でそれぞれ振動の振幅に影響があるか

なお、無理のない範囲で、再現動画(あるいは再現プロジェクト)などご提供いただけますと大変助かります。

機密コンテンツ抜きでは再現動画の撮影が困難な場合は、動画提出用のBoxストレージを用意しますので、お声がけください。

以上、よろしくお願いいたします。

[Attachment Removed]

ご返答ありがとうございます。

いただいた質問に対して、現状わかる範囲内に回答いたします。

・「Fountain」のようなNiagaraテンプレートでも再現するか

テンプレートまでは試していないが、他のNiagaraSystemも同じ現象が発生しているので、

特定のアセットの問題ではないです。

・問題の起きているNiagaraシステムが配置されている絶対のワールド座標

発生するタイミングでのワールド座標は(-308185.712171, 589988.273491, 16616.488256)となります。

・Niagaraシステムを直にレベルに配置してもジッタリングが生じるか否か

はい、直接にアクターとして同じ座標値付近に配置した場合にもジッタリングが発生します。

※原点付近に配置した場合は発生しません

・レベルシーケンス内でのアクタ位置アニメーションの有無(シーケンス内でNiagaraアクタの位置を動かしているか否か)

Niagaraアクターの位置は移動しないです。

・再現環境はWindows Editorのみか、それともStandaloneでも発生するか

両方とも発生します。

・GPU←→GPUシミュレーションの切り替え

ジッタリングが起きているNiagaraエミッタのシミュレーションモードを切り替えたときに振動に変化がみられるか

当該のNiagaraSystemに含まれるエミッタを全部GPU/CPUに切り替えてみたのですが、どちらも改善されませんでした。

・再生速度の切り替え

コンソールコマンド「slomo」を用いて、半速(slomo 0.5)、2倍速(slomo 2.0)でそれぞれ振動の振幅に影響があるか

目視範囲では振動の幅への影響がないように見えます。

<補足>

本件が発生する場面は、下記の3つが条件のようです。

- 原点から非常に遠いところに配置する

- サイズが小さい(該当のNiagaraはろうそくの火ぐらいの大きさとなります)

- カメラが非常に近い(再生するシーケンスではカメラがNiagaraから約2.583の位置から近づけていく)

更に、カメラを動かすと、振動が激しくなると確認できております。

引き続きよろしくお願いいたします。

[Attachment Removed]

お世話になっております。

ご回答ありがとうございます。

補足でまとめていただいた内容(ワールド原点から遠く、サイズが小さく、カメラが非常に近い)から、本件はやはり「[Niagara Sprite and Ribbons are shaking when very far from world [Content removed]

(こちらの事例でもズームインした際に精度が低下する挙動が報告されております)

なお、当方ではまだ現象の再現に至っておりません。単純にテンプレートエフェクトをもとに小さなエフェクトを作成し、近距離から撮影するだけでは再現できなかったため、エフェクトのサイズやカメラの距離などの条件を調整しながら、引き続き再現を試みていく所存です。いずれにせよ、本件は Niagara における精度上の限界にぶつかっている可能性が高いと考えております。大変恐縮ながら、この状況に対して直接的に精度を高められるオプションも、現時点では確認できておりません。

現在、このような条件下で取り得るワークアラウンドについて社内で確認を進めております。恐れ入りますが、追加のご案内まで今しばらくお待ちいただけますでしょうか。

以上、よろしくお願いいたします。

[Attachment Removed]

五月雨式のご連絡で申し訳ございません。

社内の Niagara 関係者から 1 点追加で確認させていただきたい点が指摘されましたので、お伺いさせてください。

当該の Niagara System は原点から遠く離れた地点に配置された際に不具合を生じるとのことでしたが、各エミッタ(パーティクル)はNiagaraシステム内の原点からどの程度オフセットされておりますでしょうか?

以上、よろしくお願いいたします。

[Attachment Removed]

お世話になっております。

当該のNiagara Systemを確認したので、頂いた質問に返答いたします。

複数のエミッターが含まれておりますが、どのエミッターも下記の状態となっています。

  • エミッターのスポーンに位置を設定していない
  • Initialize Particleに僅かなPosition Offsetを設定している(5.0以下)
  • 一部だけパーティクルの更新に僅かなCamera Offsetを設定している(2.0以下)

上記通りで、エミッターやパーティクルがNiagara Systemのローカルスペースにおいて、すごく原点から離れている状態ではありません。

引き続きよろしくお願いいたします。

[Attachment Removed]

お世話になっております。

ご返信ありがとうございます。

問題となっているNiagara Systemは現在、World Spaceで動作するよう設定されておられるかと思います。この場合、パーティクルのPositionに問題のワールド座標 (-308185.712171, 589988.273491, 16616.488256) が直接入るため、確かに小数点以下の精度はかなり心もとないです。それでも、ほとんどのユースケース(カメラがある程度引いた位置に置かれているもの)ではジッターを生じさせないはずですが、ご共有いただいたような極端な近写では問題となりそうです。

回避策として、以下の2点をご提案いたします。

■①Local Spaceに切り替える

​もし演出上問題がないのであれば、問題のパーティクルを発するエミッタをLocal Spaceに設定することが一つの解決策となります。この場合、各パーティクルのPositionはシステム相対の位置を保持するため、Floatでも精度十分であり、近写に耐えうるはずです。

■②LWCのタイルサイズを変更する

エンジン設定で Large World Coordinates (LWC)サポートを有効化している場合(※デフォルトで有効です)、 C++側の UE_LWC_RENDER_TILE_SIZE で定義されるタイルサイズを小さく切り詰めることで、精度を回復させるという手も考えられます。World SpaceのパーティクルのPositionは、属するNiagara Systemが配置されたタイル位置からの相対値を保持します。ただし、デフォルトのタイルサイズは一辺20.97 kmもあるため、今回問題となっているNiagaraシステム座標は最初のタイル領域に収まり、LWC側に座標の値を逃がすことができていません。タイルサイズを10km、5kmと切り詰めていけば、小数点の精度もかなり戻ってくると予想されます。実際、他の何社かのライセンシー様で、LWCの標準タイルサイズ値で想定しているような「超広域」のマップを作っていないことから、タイルサイズを変更し、精度の問題を解決した先行例がございます。

ただし、ひとつのマップの中にタイルの境界線が入るようになってきますと、オブジェクトが境界線をまたいで移動した際に親タイルの付け替えが発生するため、また別の問題を呼び込む恐れがあります。タイルサイズの変更はLWCを使用するすべての機能に影響を及ぼし、ゲームによっては副作用がみられる場合もあるため、十分にご検証ください。

特にNiagaraについて申し上げますと、Niagaraシステムがワールド内を移動して、あるタイルから別の​タイルに移動した際に、シミュレーションのリセットが起こる点に注意が必要です。この挙動で問題が生じました場合、 Large World Coordinate Tile Update Mode の設定を「Reset Simulation」から「Rebase or Reset Simulation」に変更することで改善が得られる場合もあります。Update Modeの設定は、各Niagaraシステムで個別に設定できるほか、Project Settingsから全体の値を設定可能です。

以上となります。

具体的な対策を提案するまでにかなり期間を要してしまい申し訳ありませんでした。

一度ご検討ください。​よろしくお願いいたします。

[Attachment Removed]

ご提案いただき、ありがとうございます。

それぞれ検証を行って、結果は下記通りです。

■①Local Spaceに切り替える

確認したところ、例のNiagaraSystemに配置したエミッタがすでにLocal Spaceになっています。

■②LWCのタイルサイズを変更する

UE_LWC_RENDER_TILE_SIZEを許容最小値の262144.0に設定してみたのですが、

例の振動の問題が改善されませんでした。

残念ながら、いただいたご提案では問題を改善できませんでした。

ただし、別の検証も同時に行って、まだ問題点が残されて、解決策を探っていますが、振動の問題を解消できました。

検証の内容手順を簡単にまとめましたので、下記に添付いたします。

次の検証項目や他に考えられる原因についてもし他に何かご提案があれば、

ご提示いただければ幸いです。

引き続きよろしくお願いいたします。

======

まずは問題のあるレベルシーケンス本来の構造について説明しておきます。

LevelSequenceに配置された各種ActorはWorldOffsetというActorにAttachされています。

WorldOffsetは再生位置に合わせて座標値が設定されています。

更にカメラも再生位置に合わせて大きい座標値が各キーに設定されています。

Levelには、LevelSequenceActorとTransformOriginActorというダミーActorが配置されています。

LevelSequenceActorはLevelSequenceがアセットとして設定されています。

TransformOriginActorは前述したWorldOffsetと同じ座標値が設定されています。

※現時点はTransformOriginActorがまだ使われていません。

この構造の中で、今回の問題を解決するための手順と各ステップ後の挙動について記載します。

1. LevelSequenceActorからシーケンサーを開いてそのまま再生する

→ 例のNiagaraSystemが振動する

2. LevelSequenceでのWorldOffsetの座標値をクリアして、カメラの各キーからもWorldOffsetの座標値を減算した結果に書き換える

→ 例のNiagaraSystemが振動しなくなるが、再生位置が原点になるので、Levelに直接に配置された背景物が表示されなくなる

3. LevelSequenceActorにTransformOriginActorをTransformOriginのActorとして設定する

→ 再生位置が正しい座標に戻るが、再びNiagaraSystemが振動してしまう

4. この状態でUWorld::SetNewWorldOrigin()を呼び出して、WorldOffsetの座標値を原点に設定する

→ 再生位置も正しくて、NiagaraSystemが振動しなくなる

現在この状態ではライトやカメラカーリングの問題などが発生して、原因調査と解決策の検証を引き続き行っていますが、

一応例のNiagaraSystemが振動する問題は上記の手順で解消できました。

[Attachment Removed]

お世話になっております。

Local Spaceの確認とタイルサイズの縮小の検証をありがとうございました。

どちらも効き目がなかったようで申し訳ありません。

Local Space でも精度由来の振動が起きているのは、描画段階で部分的に大きな値を単精度で使う部分があり、そこで精度を失っている可能性を疑っております。一方、タイルサイズの変更については、問題を完全に解消できないまでも、原理上何らか動作に影響が出ると考えておりましたが、改善が見られなかったとのことで、まだその理由を推測できずにおります。大変面目ございません。

UWorld::SetNewWorldOrigin() で振動の問題が解消された代わりに別の問題が生じた件につきましては、原点移動がもともと旧来の World Composition 向けに設計された機能であり、現在あまり積極的に保守されていないことが影響していると思われます。特に、 World Partition 環境が不安定なはずでして、本来の座標であればカメラの周りに存在しているはずのものが、誤判定によりストリームアウトを起こしたり、逆に必要なものがストリームインされないといった症状が起きると思われますが、現在のプロジェクトでWorld Partitionはお使いでしょうか?

以上、よろしくお願いいたします。

[Attachment Removed]

引き続きの調査・ご対応ありがとうございます。

現状本スレッドの問題が発生しているプロジェクトではWorld Partitionを利用しております。

以上、よろしくお願いいたします。

[Attachment Removed]

お世話になっております。

ご返信ありがとうございます。

SetNewWorldOrigin() は、ワールド原点の移し替え(リベース)のために、ワールド内のすべてのアクター(プレイヤーを含む)を指定したオフセット分だけ平行移動させる機能です。World Partitionをお使いの場合、プレイヤーはストリーミングの基準点(ストリーミングソース)も兼ねているため、その位置も動きます。しかし World Partition システムそのものは、このリベースぶんを考慮しないため、World Partitionの視点からはプレイヤーが"ファストトラベル"を実行したのと同じ扱いとなります。その結果、もともとプレイヤーがいたであろう(-300000, 590000, 17000)付近のタイルに属していたアクター群がストリームアウトされ、ライトやカメラ、一部背景物などに影響が出たのではないかと推測しております。

World PartitionをSetNewWorldOrigin()の機能に正規対応させることは非常に困難であるため、かなりトリッキーな対応ではありますが、

・あくまでこの問題が発生しているシーケンス(カットシーン)一か所だけで使う

・SetNewWorldOrigin()で書き換えたワールド原点は、演出後に元に戻す(「リベースしたまま、通常のゲームプレイに戻す」ことは行わない

ことを前提に、エンジンを改造して、演出中に World Partition のストリーミング機能を一時的に止めることで問題の回避を図れないかということを考えております。

改造箇所は以下のとおりです。

Engine/Source/Runtime/Engine/Public/WorldPartition/WorldPartitionSubsystem.h L84付近

/** Returns true if world partition is done streaming levels, adding them to the world or removing them from the world. */
       UFUNCTION(BlueprintCallable, Category = Streaming)
       ENGINE_API bool IsAllStreamingCompleted();
 
  +    UFUNCTION(BlueprintCallable, Category = Streaming)
  +    void SetStreamingPaused(bool bInPaused) { bStreamingPaused = bInPaused; }
  +        
  +    UFUNCTION(BlueprintPure, Category = Streaming)
  +    bool IsStreamingPaused() const { return bStreamingPaused; }

同 L168行目付近

static ENGINE_API void UpdateStreamingStateInternal(const UWorld* InWorld, UWorldPartition* InWorldPartition = nullptr);
       static bool IsServer(const UWorld* InWorld);
  +
  +    // When true, OnUpdateStreamingState() is skipped to freeze the loaded cell set.
  +    bool bStreamingPaused = false;

Engine/Source/Runtime/Engine/Private/WorldPartition/WorldPartitionSubsystem.cpp L1115付近

void UWorldPartitionSubsystem::OnUpdateStreamingState()
       {
           SCOPE_CYCLE_COUNTER(STAT_WorldPartitionUpdateStreamingState);
  +
  +        // 早期リターン追加
  +        if (bStreamingPaused)
  +        {
  +            return;
  +        }
  
           UWorldPartitionSubsystem::UpdateStreamingStateInternal(GetWorld());
       }

以上です。

  1. 上記の改造で追加されるBPノード/C++関数を使ってまずWPのストリーミング処理にポーズをかけ、
  2. SetNewWorldOrigin()を呼び出して原点付近へ移動
  3. シーケンスを再生
  4. 再生終了後、原点を元の座標(0, 0, 0)に戻す
  5. ポーズを解除

という使い方を想定しております。当方の簡単なテストケースでは無事に動作し、不意なストリームアウトなどを防止できたのですが、本格的なプロジェクトでは十分な検証が必要になると思われます。特に、ポーズ中は新規セルのロードも停止するため、カットシーンで見せる範囲の(アセットが含まれる)セルはポーズをかける前にロードが完了している必要がある点にご注意ください。

以上、よろしくお願いいたします。

(※6/29 16:56 説明を一部編集)

[Attachment Removed]

ご返答ありがとうございます。

上記内容についてチーム内で検討してみます。

[Attachment Removed]

お世話になっております。

ご提示いただいた手法で試してみたのですが、背景物が非表示になってしまう問題は解消できませんでした。

一方で、これまでいただいた情報をもとに色々と検証を行い、この現象が発生する原因や影響範囲について、理解を深めることができました。

現時点では、この現象が弊社プロジェクトで発生する場面は限られており、影響範囲も広くないと考えております。

そのため、今後同様の問題が多発しない限り、今回の問題については例外的なケースとして扱い、Niagara側の調整など、個別対応で回避したいと考えております。

ですので、本件については、スレッドをクローズしていただいて問題ありません。

多くのご意見と情報をいただき、ありがとうございました。

今後ともよろしくお願いいたします。

[Attachment Removed]

お世話になっております。

こちらで提示した手法が本番プロジェクトでは問題を解決できなかった旨、承知いたしました。

本件をまとめますと、

・ご提示いただいた座標値では理論上、精度の欠落が生じ、ズーム具合の次第で問題が生じる。

・ぎりぎりまで大きな座標計算を避ける Local Space 設定でも本件の精度不足は防げなかった。

・LWCのタイル分割サイズ( UE_LWC_RENDER_TILE_SIZE )の変更は、過去の事例では通用した例があったが、本件では助けにならなかった。

・SetNewWorldOrigin()による全体位置補正は、World Partitionのストリーミングが対応していないため、予想外のストリーミングイン/アウトを発生させる。

という形となりました。お役に立てず大変申し訳ございませんでした。

それでは、大変恐縮ながら本件はCloseとさせていただきますが、

また新たなご不明点や不具合など生じましたら、ぜひお問い合わせいただければと存じます。

以上、よろしくお願いいたします。

[Attachment Removed]