Hardware Ray Tracing有効時の画面内描画判定の代替案について

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

現在、対象が画面内に描画されているか否かで、カメラのロックオン維持を判定する実装を行っています。

その判定用として、`WasRecentlyRendered` を参考に以下の `WasRecentlyRenderedOnScreen` という関数を実装しました。

const bool UCameraUtility::WasRecentlyRenderedOnScreen(const UPrimitiveComponent* TargetPrimitiveComponent, const float Tolerance /* = 0.2f*/)
{
  if (!IsValid(TargetPrimitiveComponent))
  {
    return false;
  }
 
  if (const UWorld* const World = TargetPrimitiveComponent->GetWorld())
  {
    const float RenderTimeThreshold = FMath::Max(Tolerance, World->DeltaTimeSeconds + UE_KINDA_SMALL_NUMBER);
    const float LastRenderTimeOnScreen = TargetPrimitiveComponent->GetLastRenderTimeOnScreen();
 
    return World->TimeSince(LastRenderTimeOnScreen) <= RenderTimeThreshold;
  }
 
  return false;
}

【発生している問題】

以前はこの実装により、カメラと対象の間にオブジェクトがあり、対象が完全に遮蔽された際には想定通りロックオンが解除されていました。

しかし、直近で Hardware Ray Tracing を有効にしたところ、対象が隠れてもロックオンが解除されなくなってしまいました。

【原因の推測】

確認したところ、Hardware Ray Tracing 有効時は対象が遮蔽されていても GetLastRenderTimeOnScreen が常時更新される仕様になっているようです。

これは、Ray Tracing の光や影の計算において、直接画面に描画されていなくてもフラスタム内に存在すればレンダリング対象として処理されるためだと推測しています。

【ご相談】

本題となりますが、何らかの RenderData 等を用いて、以前と同じ(遮蔽時にロックオンが解除される)挙動を実現する代替案やアプローチはないでしょうか?

(※LineTrace等の物理判定ではなく、描画ベースでの判定方法を探しています)

お手数をおかけしますが、よろしくお願いいたします。

[Attachment Removed]

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

本件確認させていただいたところ、

Raytracing時には以下にあるような距離設定の判定で強制的にLastRenderTimeが更新されるような形となっており、

この時LastRenderTimeOnScreenに関しても更新される形となっているようでした。

r.RayTracing.DynamicGeometryLastRenderTimeUpdateDistance

描画されていない判定を行うためにはLastRenderTimeOnScreenの仕組みが適切かと思われるため、同様の仕組みでRaytracing時に更新しない専用のフラグを作成する、もしくは、

Raytracing時もLastRenderTimeOnScreenの方は更新しないようにエンジン改造を行う対応が考えられそうです。

・LastRenderTimeOnScreenに関して

主にSkinned MeshのbRecentlyRendered判定(Tick)と、Texture Streaming等の判定に利用されているようでした。

Tick関連に関しては各コンポーネントのbUseScreenRenderStateForUpdateが有効の場合に考慮される形となっており、

またデフォルト設定だとFalse(LastRenderTimeが利用される)となっているため影響は少ない見込みです。

Texture Streamingではメインの描画に映っていない場合にテクスチャがぼけてしまう可能性がありますが、プロジェクト運用上許容できる場合は専用のフラグを用意せずに以下のような対応が行えそうです。

\Engine\Source\Runtime\Renderer\Private\RayTracing\RayTracing.cpp

if (DistanceToView < LastRenderTimeUpdateDistance)
{
    // Update LastRenderTime for components so that visibility based ticking (like skeletal meshes) can get updated
    // We are only doing this for dynamic geometries now
    SceneInfo->LastRenderTime = CurrentWorldTime;
    // SceneInfo->UpdateComponentLastRenderTime(CurrentWorldTime, /*bUpdateLastRenderTimeOnScreen=*/true);
    SceneInfo->UpdateComponentLastRenderTime(CurrentWorldTime, /*bUpdateLastRenderTimeOnScreen=*/false);
}

お手数ですが上記対応をご検討頂けますと幸いです。

[Attachment Removed]

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

ご確認ありがとうございます。

現状のプロジェクトの状況を確認し、ご提案頂いた更新フラグを`false`にする方針で進めていこうと思います。

進展がありましたら再度ご連絡させていただきます。

恐れ入りますが、お待ちいただけると幸いです。

よろしくお願いいたします。

[Attachment Removed]

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

先日はご回答いただき誠にありがとうございました。

ご提示いただいた方法で検証したところ、無事に解決できそうです。

(実際のプロジェクトに本対応として組み込むかは、現在チーム内で検討を進めておりますのでこちらに関してもまた質問するかもしれないです。)

今回は表題の件から若干逸れてしまうのですが、GetLastRenderTimeOnScreenに関連して再度ご相談させてください。

前回のRay Tracingの件とは別に、現在対象物にCustom Depthを使用しております。

その影響でオクルージョンカリングが効かず、遮蔽されていても下記のコードの通りLastRenderTimeOnScreenが常に更新されてしまう状態です。

(※Custom Depthの仕様上、これ自体は問題ないという認識です)

// SceneVisibility.cpp L: 2104~
 
// Update the last component render time only if we know for certain the primitive is un-occluded.
if ((View.PrimitiveDefinitelyUnoccludedMap[BitIndex] || View.Family->EngineShowFlags.Wireframe) && !PrimitiveSceneProxy->IsAlwaysVisible())
{
	const bool bUpdateLastRenderTimeOnScreen = true;
	PrimitiveSceneInfo->UpdateComponentLastRenderTime(CurrentWorldTime, bUpdateLastRenderTimeOnScreen);
}

今回お聞きしたいのは、このCustom Depthを使用している状態からでも、「実際に画面に映っていない(遮蔽されている)」ことを判定し、何らかのデータを取得・処理する良いアプローチはないかという点です。

必ずしもGetLastRenderTimeOnScreenを使用することにこだわっているわけではありませんので、別の判定アプローチやTipsなどがありましたら、ご教示いただけますと幸いです。

よろしくお願いいたします。

[Attachment Removed]

お世話になっております。 ご返答が遅くなってしまい、申し訳ございません。 また、具体的な代替案をご提示いただき誠にありがとうございます。

教えていただいた「類似のBoundsを持つ判定用の軽量メッシュをアタッチする」という手法、大変参考になりました。

実は私自身も、Boundsを利用して判定を行うアイデア自体は頭をよぎっていたのですが、定石として正しいアプローチなのか迷いがあり、実装に踏み切れずに降りました。 今回、既存の仕組みを活かす有効な手段としてご提案いただけたことで、安心してこの方向で進めることができそうです。

また、私の検討段階ではBoundsの活用ばかりに思考が向いており、「判定用に"軽量化"したメッシュをアタッチして代用する」というランタイムコストを抑える実践的な視点が抜け落ちておりました。パフォーマンス面まで配慮されたアドバイスをいただき、大変感謝しております。

いただいた案をベースに、実装と検証を進めてみようと思います。

この度は貴重なご助言をいただき、ありがとうございました。

[Attachment Removed]

ご確認ありがとうございます。

更新フラグをfalseにする方針の旨承知いたしました。

検証頂いたうえで何か問題等がありましたら再度ご相談頂ければと思います。

お手数おかけしますが、よろしくお願いいたします。

[Attachment Removed]

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

CustomDepthを利用いただいている場合はオクルージョン対象から外れてしまいますが、

取得したい情報としては、オクルージョンクエリのような仕組みが必要になってしまうと思われます。

実装やランタイムコストを考えると、可能であれば既存の仕組みを使い行った方が効率的かと思いますので、

1つの案としては類似のBoundsを持つ判定用の軽量メッシュをアタッチしておく方法が挙げられそうです。

※もしくは既存のメッシュでCustomDepthから外せるものがあれば、それを判定用にする

オクルージョン判定は行いつつ不可視にしたい場合は、Render in MainPassやRender in DepthPass等のVisible~, Render~フラグや、

Use as Occluder、メッシュのSupport Ray tracingをDisableにすることで軽量に実装できる見込みです。

お手数おかけしますが、こちらご検討いただけますと幸いです。

よろしくお願いいたします。

[Attachment Removed]

ご丁寧にありがとうございます。実装の参考になりましたら幸いです。

本件はクローズとさせていただきますが、また何かございましたらお気軽にご相談ください。

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

[Attachment Removed]