ue-materials-rendering

Use when the user is working with material, shader, MID, dynamic material, material instance, post-process, render target, parameter collection, decal, Nanite, Lumen, or rendering in Unreal Engine. See references/material-parameter-reference.md for parameter patterns and references/post-process-settings.md for post-process settings. For particle rendering, see ue-niagara-effects.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "ue-materials-rendering" with this command: npx skills add quodsoler/unreal-engine-skills/quodsoler-unreal-engine-skills-ue-materials-rendering

UE Materials and Rendering

You are an expert in Unreal Engine's material and rendering systems. You provide accurate C++ patterns for dynamic materials, parameter collections, post-process, render targets, decals, and UE5 rendering features (Nanite, Lumen, Virtual Shadow Maps).


Step 1: Read Project Context

Read .agents/ue-project-context.md before giving advice. From it, extract:

  • Engine version — UE5.0–5.4 APIs differ (e.g., SetNaniteOverride added in 5.x; CopyScalarAndVectorParameters signature changed in 5.7)
  • Target platforms — Mobile requires forward rendering; many post-process features are desktop-only
  • Rendering settings — Nanite/Lumen enabled status affects which material features are safe
  • Module names — needed for correct #include paths and Build.cs dependencies

If the context file is missing, ask for engine version and target platforms before proceeding.


Step 2: Clarify the Rendering Need

Ask which area the user needs:

  1. Dynamic Material Instances (MID) — runtime parameter changes on mesh components
  2. Material Parameter Collections — global parameters shared across all materials
  3. Post-Process — bloom, exposure, color grading, DOF, AO via volumes or components
  4. Render Targets — scene capture, minimap, security camera, canvas drawing
  5. Decals — deferred decals spawned at runtime, fade, sort order
  6. Rendering Pipeline / UE5 Features — Nanite, Lumen, Virtual Shadow Maps, custom depth/stencil

Multiple areas can be combined.


Core Patterns

1. Dynamic Material Instances (MID)

Creation

Pattern A — from UMaterialInterface (standalone, not tied to a component slot):

// Header
UPROPERTY()
TObjectPtr<UMaterialInstanceDynamic> MyMID;

// Implementation — call once (BeginPlay or equivalent), cache the result
UMaterialInterface* BaseMat = LoadObject<UMaterialInterface>(
    nullptr, TEXT("/Game/Materials/M_MyBase.M_MyBase"));

MyMID = UMaterialInstanceDynamic::Create(BaseMat, this);

Pattern B — via component slot (preferred for meshes):

// UMeshComponent::CreateDynamicMaterialInstance creates a MID for the given
// element index and assigns it to the slot automatically.
// Signature: CreateDynamicMaterialInstance(int32 ElementIndex,
//                UMaterialInterface* SourceMaterial = nullptr,
//                FName OptionalName = NAME_None)

UMaterialInstanceDynamic* MID = MeshComponent->CreateDynamicMaterialInstance(
    0,            // element index
    nullptr,      // nullptr = use the slot's current material as parent
    TEXT("MyMID") // optional debug name
);

Source: MaterialInstanceDynamic.h, PrimitiveComponent.h. Build.cs: "Engine".

Setting Parameters

MyMID->SetScalarParameterValue(TEXT("Opacity"), 0.5f);
MyMID->SetVectorParameterValue(TEXT("BaseColor"), FLinearColor(1.f, 0.2f, 0.1f, 1.f));
MyMID->SetVectorParameterValue(TEXT("Offset"), FLinearColor(0.f, 0.f, 100.f, 0.f)); // XYZ via FLinearColor
MyMID->SetTextureParameterValue(TEXT("DamageMask"), MyTexture);
MyMID->SetTextureParameterValue(TEXT("SecurityFeed"), RenderTargetAsset); // RT as texture

Full setter signatures from MaterialInstanceDynamic.h:

void SetScalarParameterValue(FName ParameterName, float Value);
void SetVectorParameterValue(FName ParameterName, FLinearColor Value);  // Pass FLinearColor; no implicit conversion from FVector
void SetTextureParameterValue(FName ParameterName, UTexture* Value);

High-Frequency Updates — Index-Based API

When setting dozens of parameters per frame (rare but valid), use index caching:

// In BeginPlay or initialization — call once per parameter name:
int32 OpacityIndex = -1;
MyMID->InitializeScalarParameterAndGetIndex(TEXT("Opacity"), 1.0f, OpacityIndex);

// In Tick — use index, no name lookup:
if (OpacityIndex >= 0)
{
    MyMID->SetScalarParameterByIndex(OpacityIndex, NewOpacity);
}

Index is invalidated if the parent material changes. Do not share indices across different MID instances.

MID Lifecycle and GC

MIDs are UObjects — they are garbage collected when unreferenced. To keep a MID alive:

// In your class header — must be UPROPERTY to prevent GC
UPROPERTY()
TObjectPtr<UMaterialInstanceDynamic> CachedMID;

Never store MIDs in raw pointers or local variables across frames.

Additional MID Operations

// Lerp between two instances' scalar/vector params
MyMID->K2_InterpolateMaterialInstanceParams(InstanceA, InstanceB, Alpha);

// Assign Nanite-compatible override material (UE5)
MyMID->SetNaniteOverride(NaniteCompatibleMaterial);

2. Material Parameter Collections

UMaterialParameterCollection is an asset holding scalar and vector parameters accessible from any material via CollectionParameter expression. One GPU buffer update propagates to all referencing materials. Source: MaterialParameterCollection.h, MaterialParameterCollectionInstance.h.

Setting Parameters at Runtime

// MyCollection is a UPROPERTY(EditAnywhere) pointing to the MPC asset.
UPROPERTY(EditAnywhere, Category="Rendering")
TObjectPtr<UMaterialParameterCollection> GlobalRenderingCollection;

// At runtime — get the per-world instance and set values:
void AMyActor::UpdateGlobalWeather(float RainIntensity, FLinearColor FogColor)
{
    UMaterialParameterCollectionInstance* Instance =
        GetWorld()->GetParameterCollectionInstance(GlobalRenderingCollection);

    if (Instance)
    {
        Instance->SetScalarParameterValue(TEXT("RainIntensity"), RainIntensity);
        Instance->SetVectorParameterValue(TEXT("FogColor"), FogColor);
    }
}

Both setters return false if the parameter name is not found. Names are case-sensitive. Limits: max 1024 scalars + 1024 vectors per collection; no texture parameters; global to the world instance.


3. Post-Process Volumes

APostProcessVolume wraps FPostProcessSettings and controls how the camera is rendered when inside (or globally when bUnbound = true).

From PostProcessVolume.h:

struct FPostProcessSettings Settings; // The settings payload
float Priority;      // Higher priority wins on overlap (undefined order when equal)
float BlendRadius;   // World-space blend distance in cm (only when bUnbound = false)
float BlendWeight;   // 0 = no effect, 1 = full effect
uint32 bEnabled:1;
uint32 bUnbound:1;   // true = applies globally regardless of camera position

Modifying a Post-Process Volume from C++

// Assume PostProcessVolume is assigned or found:
APostProcessVolume* PPV = /* find or spawn */;

// Enable and configure
PPV->bEnabled = true;
PPV->bUnbound = true; // global effect
PPV->BlendWeight = 1.0f;

// Bloom
PPV->Settings.bOverride_BloomIntensity = true;
PPV->Settings.BloomIntensity = 0.5f;

// Auto Exposure
PPV->Settings.bOverride_AutoExposureMinBrightness = true;
PPV->Settings.AutoExposureMinBrightness = 0.1f;
PPV->Settings.bOverride_AutoExposureMaxBrightness = true;
PPV->Settings.AutoExposureMaxBrightness = 2.0f;

// Depth of Field (Cinematic DOF)
PPV->Settings.bOverride_DepthOfFieldFstop = true;
PPV->Settings.DepthOfFieldFstop = 2.8f;
PPV->Settings.bOverride_DepthOfFieldFocalDistance = true;
PPV->Settings.DepthOfFieldFocalDistance = 300.0f; // cm

// Ambient Occlusion
PPV->Settings.bOverride_AmbientOcclusionIntensity = true;
PPV->Settings.AmbientOcclusionIntensity = 0.5f;

// Vignette
PPV->Settings.bOverride_VignetteIntensity = true;
PPV->Settings.VignetteIntensity = 0.4f;

// Color Grading
PPV->Settings.bOverride_ColorSaturation = true;
PPV->Settings.ColorSaturation = FVector4(1.2f, 1.0f, 0.8f, 1.0f); // per-channel RGBA
PPV->Settings.bOverride_FilmSlope = true;
PPV->Settings.FilmSlope = 0.88f; // 0–1 (default 0.88)

Every field in FPostProcessSettings has a corresponding bOverride_* bool that must be set to true for the value to take effect. See references/post-process-settings.md for a full field reference.

Post-Process Materials (Blendables)

Material Domain must be "Post Process". Add via:

PPV->AddOrUpdateBlendable(PostProcessMaterial, 1.0f); // weight 0.0–1.0

UPostProcessComponent (Actor-Owned)

// Constructor
PostProcessComp = CreateDefaultSubobject<UPostProcessComponent>(TEXT("PostProcess"));
PostProcessComp->bUnbound = true;
PostProcessComp->Priority = 5.0f;

// Runtime
PostProcessComp->Settings.bOverride_BloomIntensity = true;
PostProcessComp->Settings.BloomIntensity = 1.5f;

Includes: "Components/PostProcessComponent.h", "Engine/PostProcessVolume.h", "Engine/Scene.h".


4. Render Targets

Creating a Render Target in C++

#include "Engine/TextureRenderTarget2D.h"
#include "Kismet/KismetRenderingLibrary.h"

// Option A — via UKismetRenderingLibrary (handles resource init automatically)
UTextureRenderTarget2D* RT = UKismetRenderingLibrary::CreateRenderTarget2D(
    this,          // WorldContextObject
    512,           // Width
    512,           // Height
    RTF_RGBA16f,   // Format (see ETextureRenderTargetFormat)
    FLinearColor::Black,
    false          // bAutoGenerateMipMaps
);

// Option B — manual creation
UTextureRenderTarget2D* RT = NewObject<UTextureRenderTarget2D>(this);
RT->InitCustomFormat(512, 512, PF_FloatRGBA, /*bInForceLinearGamma=*/true);
RT->UpdateResourceImmediate(/*bClearRenderTarget=*/true);

ETextureRenderTargetFormat values from TextureRenderTarget2D.h:

FormatChannelsBits/ChannelUse Case
RTF_RGBA8RGBA8 fixedLDR color, UI
RTF_RGBA8_SRGBRGBA8 fixedsRGB color
RTF_RGBA16fRGBA16 floatHDR color (default)
RTF_RGBA32fRGBA32 floatHigh precision data
RTF_R16fR16 floatSingle channel data
RTF_RGB10A2RGB+A10+2 bitDisplay output

Scene Capture (Security Camera / Minimap)

#include "Components/SceneCaptureComponent2D.h"

// In actor constructor
SceneCapture = CreateDefaultSubobject<USceneCaptureComponent2D>(TEXT("SceneCapture"));
SceneCapture->SetupAttachment(RootComponent);
SceneCapture->FOVAngle = 90.f;
SceneCapture->CaptureSource = ESceneCaptureSource::SCS_FinalColorLDR; // or SCS_SceneColorHDR
SceneCapture->bCaptureEveryFrame = true;  // continuous update

// Assign a render target asset or a runtime-created one
SceneCapture->TextureTarget = MyRenderTargetAsset;

// Limit what's captured for performance
SceneCapture->ShowFlags.SetAtmosphere(false);
SceneCapture->ShowFlags.SetFog(false);

Drawing a Material to a Render Target

// Renders a full-screen quad with Material applied to TextureTarget.
// This is expensive (sets render target each call); use canvas API for batching.
UKismetRenderingLibrary::DrawMaterialToRenderTarget(
    this,         // WorldContextObject
    RT,           // UTextureRenderTarget2D*
    MyMaterial    // UMaterialInterface*
);

Canvas Drawing (Batched)

UCanvas* Canvas;
FVector2D CanvasSize;
FDrawToRenderTargetContext Context;

UKismetRenderingLibrary::BeginDrawCanvasToRenderTarget(this, RT, Canvas, CanvasSize, Context);

// Draw primitives to Canvas here...
Canvas->K2_DrawMaterial(MyMaterial, FVector2D(0, 0), CanvasSize, FVector2D(0, 0), FVector2D(1, 1));

UKismetRenderingLibrary::EndDrawCanvasToRenderTarget(this, Context);

UCanvasRenderTarget2D — subclass of UTextureRenderTarget2D with a built-in OnCanvasRenderTargetUpdate delegate. Use for automatic 2D canvas redraw (minimaps, runtime texture painting) instead of manual BeginDrawCanvasToRenderTarget calls.

Reading Pixels (GPU Stall — Offline Only)

// WARNING: stalls GPU pipeline. Editor tools / screenshot only, never per-frame.
FColor Pixel = UKismetRenderingLibrary::ReadRenderTargetPixel(this, RT, X, Y);
TArray<FColor> Pixels;
UKismetRenderingLibrary::ReadRenderTarget(this, RT, Pixels);               // whole RT, 8-bit sRGB
FLinearColor Raw = UKismetRenderingLibrary::ReadRenderTargetRawPixel(this, RT, X, Y);

5. Decals

UDecalComponent projects a material onto surfaces. Key API from DecalComponent.h:

void SetDecalMaterial(UMaterialInterface* NewDecalMaterial);
UMaterialInstanceDynamic* CreateDynamicMaterialInstance(); // MID on the decal
void SetFadeOut(float StartDelay, float Duration, bool DestroyOwnerAfterFade = true);
void SetFadeIn(float StartDelay, float Duration);
void SetSortOrder(int32 Value);  // higher = draws on top
void SetLifeSpan(float LifeSpan);
FVector DecalSize; // local-space extent (not component scale)

Spawning Decals at Runtime

// 0.0f lifespan = persistent; >0.0f = auto-destroy after N seconds
UDecalComponent* Decal = UGameplayStatics::SpawnDecalAtLocation(
    this, DecalMaterial, FVector(200.f), HitLocation, HitNormal.Rotation(), 0.0f);

// Dynamic parameters on the decal
UMaterialInstanceDynamic* DecalMID = Decal->CreateDynamicMaterialInstance();
DecalMID->SetScalarParameterValue(TEXT("Opacity"), 0.8f);

DBuffer vs Non-DBuffer Decals

  • DBuffer (Translucent + DBuffer enabled): writes before lighting, affects diffuse/normals/roughness. Enable via Project Settings > Rendering > DBuffer Decals.
  • Non-DBuffer: rendered after lighting, emissive/opacity only; cheaper but limited.

For level-placed decals, use ADecalActor (a wrapper around UDecalComponent). For runtime-spawned decals, prefer UGameplayStatics::SpawnDecalAtLocation or SpawnDecalAttached.


6. Nanite and Lumen (UE5)

Nanite

Nanite is UE5's virtualized geometry system. Material compatibility rules:

FeatureNanite Compatible
Opaque materialsYes
Two-sided materialsYes
Masked materialsYes (with r.Nanite.AllowMaskedMaterials=1)
Translucent materialsNo — falls back to non-Nanite path
World Position Offset (WPO)Supported in UE 5.1+ (bEvaluateWorldPositionOffset on mesh)
Pixel Depth OffsetNo
Custom vertex normals via shaderLimited

Check at runtime:

// Check if a static mesh component is using Nanite (IsNaniteEnabled is on UStaticMesh, not on the component)
bool bIsNanite = StaticMeshComponent->GetStaticMesh() && StaticMeshComponent->GetStaticMesh()->IsNaniteEnabled();

Override material for Nanite path:

MyMID->SetNaniteOverride(NaniteCompatibleMaterial);

Lumen

Lumen is UE5's dynamic GI and reflections system. Emissive surfaces can act as lights. Translucent surfaces are not traced by default. Control quality via post-process settings:

PPV->Settings.bOverride_LumenReflectionQuality = true;
PPV->Settings.LumenReflectionQuality = 1.0f;         // 0–4

PPV->Settings.bOverride_LumenSceneDetail = true;
PPV->Settings.LumenSceneDetail = 1.0f;               // surface cache resolution multiplier

PPV->Settings.bOverride_LumenSceneLightingQuality = true;
PPV->Settings.LumenSceneLightingQuality = 1.0f;

Performance: r.Lumen.SurfaceCache.UpdateDownsampleFactor controls cache update rate.

Deferred vs Forward Rendering

Deferred vs Forward: UE5 desktop uses deferred rendering by default — geometry writes to GBuffer, then lighting is computed per-pixel. Forward rendering (mobile, VR) processes lighting per-object, supports MSAA, but limits dynamic light count. Set via Project Settings > Rendering > Forward Shading.

Scalability: Use Scalability::SetQualityLevels() (in Scalability.h) or console commands such as sg.PostProcessQuality 0-3 to adjust rendering quality at runtime. Configure presets in BaseScalability.ini.

Virtual Shadow Maps (VSM)

  • WPO materials: enable "Evaluate World Position Offset" in the material's Details panel (material editor setting, not a C++ property) for correct VSM shadows.
  • Masked materials: opacity masks respected correctly.
  • Decals do not cast VSM shadows.

Custom Depth / Stencil (Outlines and Effects)

MeshComponent->SetRenderCustomDepth(true);
MeshComponent->SetCustomDepthStencilValue(1); // 0–255
// Sample CustomDepth / CustomStencil nodes in a post-process material for outlines, X-ray, highlight effects.

Enable: Project Settings > Rendering > Custom Depth-Stencil Pass > Enabled with Stencil.


Common Mistakes and Anti-Patterns

Creating MIDs every frame — Each CreateDynamicMaterialInstance call allocates a new GPU resource. Create once in BeginPlay, cache, update in Tick:

// BeginPlay
CachedMID = MeshComponent->CreateDynamicMaterialInstance(0);

// Tick
if (CachedMID) { CachedMID->SetScalarParameterValue(TEXT("Time"), GetWorld()->TimeSeconds); }

Not caching MID as UPROPERTY — Raw UMaterialInstanceDynamic* is invisible to GC and collected on the next GC pass. Use UPROPERTY() TObjectPtr<UMaterialInstanceDynamic> CachedMID;.

Wrong parameter names — Names are case-sensitive exact matches. "basecolor", "Base Color", and "Base_Color" all silently fail if the material uses "BaseColor".

Render target resolution — Match resolution to use: 256–512 for minimap/security camera, 512 max for mirrors; use planar reflections for large mirrors. Full-screen: use bMainViewResolution on USceneCaptureComponent2D.

Reading render target pixels per frameReadRenderTargetPixel stalls the GPU pipeline. Never call per frame. Use FRHIGPUTextureReadback for async non-stalling reads.

MIDs on replicated actors — MIDs are client-local. Do not replicate the MID pointer. Replicate the scalar/vector values and re-apply via OnRep functions on each client.

Post-process bOverride not set — Every FPostProcessSettings field requires its paired bOverride_* bool set to true. Setting a value without the override is a silent no-op.

Nanite translucency fallback — Translucent materials on Nanite meshes revert the full mesh to non-Nanite rendering. Split into separate opaque and translucent components.


Required Build.cs Dependencies

PublicDependencyModuleNames.AddRange(new string[]
{
    "Engine",           // Materials, render targets, UTextureRenderTarget2D
    "RenderCore",       // Low-level render utilities
    "RHI",              // RHI types (EPixelFormat, etc.)
});

// For UKismetRenderingLibrary:
// Already available via "Engine" — no separate module needed.

Related Skills

  • ue-cpp-foundations — UObject management, UPROPERTY, TObjectPtr, garbage collection
  • ue-actor-component-architecture — setting up components (UDecalComponent, USceneCaptureComponent2D, UPostProcessComponent)
  • ue-niagara-effects — particle materials use MIDs; parameter passing into Niagara from C++
  • ue-project-context — engine version, target platforms, rendering feature flags

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

ue-character-movement

No summary provided by upstream source.

Repository SourceNeeds Review
General

ue-editor-tools

No summary provided by upstream source.

Repository SourceNeeds Review
General

ue-sequencer-cinematics

No summary provided by upstream source.

Repository SourceNeeds Review
ue-materials-rendering | V50.AI