Skip to content
PRODUCT GITHUB  ↗

Levels of Detail

Every Realtime Mesh has at least one LOD (LOD 0). You can add more so the renderer swaps to cheaper geometry as the mesh shrinks on screen. LODs are completely independent — each holds its own section groups and sections — so a high-detail LOD can be organized entirely differently from a low-detail one. This page covers the LOD API on URealtimeMesh. For where LODs sit in the object model, see Structure.

The example throughout is ARealtimeMeshExample_Simple_LODs (Source/RealtimeMeshExamples/Private/Simple/RealtimeMeshExample_Simple_LODs.cpp), which builds four LODs, each a differently rotated and colored box so LOD switches are easy to see.


FRealtimeMeshLODConfig (in Public/Interface/Core/RealtimeMeshLODConfig.h) is small:

struct FRealtimeMeshLODConfig
{
bool bIsVisible; // default true
float ScreenSize; // default 0.0
};
  • ScreenSize is the screen-size threshold at which this LOD activates, when no higher-detail LOD is active. Higher values keep the LOD active at larger on-screen sizes; the value is the projected screen fraction, so lower LODs use progressively smaller thresholds.
  • bIsVisible toggles the whole LOD.

The constructor takes the screen size: FRealtimeMeshLODConfig(0.5f).


LOD 0 always exists. Tighten its screen size and add further LODs with progressively smaller thresholds:

URealtimeMeshSimple* RealtimeMesh =
GetRealtimeMeshComponent()->InitializeRealtimeMesh<URealtimeMeshSimple>();
RealtimeMesh->SetupMaterialSlot(0, "PrimaryMaterial");
// LOD 0 already exists — reconfigure it so the lower LODs get a chance to show.
RealtimeMesh->UpdateLODConfig(0, FRealtimeMeshLODConfig(0.75f));
for (int32 LODIndex = 0; LODIndex < 4; LODIndex++)
{
// Add LODs 1..3 with shrinking screen-size thresholds (0.5, 0.25, 0.125).
if (LODIndex > 0)
{
RealtimeMesh->AddLOD(FRealtimeMeshLODConfig(FMath::Pow(0.5f, LODIndex)));
}
// ...build this LOD's geometry (below)...
}

The relevant calls (all BlueprintCallable, see Public/RealtimeMesh.h):

  • FRealtimeMeshLODKey AddLOD(const FRealtimeMeshLODConfig& Config) — appends a new LOD and returns its key.
  • void UpdateLODConfig(FRealtimeMeshLODKey LODKey, const FRealtimeMeshLODConfig& Config) — changes an existing LOD’s config (this is how you reconfigure LOD 0, which you can’t add).
  • void RemoveTrailingLOD() — removes the last LOD. Call it repeatedly to shrink the chain; there’s no arbitrary-index removal (LODs stay contiguous from 0).
  • TArray<FRealtimeMeshLODKey> GetLODs() — the current LOD keys.

Section groups are keyed by LOD, so you populate each LOD by creating its groups against that LOD’s key. In the example, each LOD gets its own box:

// Section groups are keyed per LOD.
const FRealtimeMeshBufferSetKey GroupKey =
FRealtimeMeshBufferSetKey::Create(LODIndex, FName("Box"));
FRealtimeMeshStreamSet StreamSet;
URealtimeMeshBasicShapeTools::AppendBoxMesh(
StreamSet, FVector3f(100.0f, 100.0f, 100.0f), Transforms[LODIndex], 0, Colors[LODIndex]);
RealtimeMesh->CreateBufferSet(GroupKey, StreamSet);
RealtimeMesh->UpdateSectionConfig(
FRealtimeMeshSectionKey::CreateForPolyGroup(GroupKey, 0),
FRealtimeMeshSectionConfig(0));

Because the groups are independent per LOD, nothing forces them to match — a detailed LOD might use several groups/sections while a cheap LOD collapses to one. GetBufferSets(LODKey) enumerates the groups in a given LOD. See Section Groups & Sections for the group/section API and Materials for slot setup.

Screen-size-driven LOD selection is a rendering concern; how the mesh chooses and blends LODs at draw time is covered in Rendering. This page is only about authoring the LOD data.