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.
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).
Read .agents/ue-project-context.md before giving advice. From it, extract:
SetNaniteOverride added in 5.x; CopyScalarAndVectorParameters signature changed in 5.7)#include paths and Build.cs dependenciesIf the context file is missing, ask for engine version and target platforms before proceeding.
Ask which area the user needs:
Multiple areas can be combined.
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".
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);
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.
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.
// Lerp between two instances' scalar/vector params
MyMID->K2_InterpolateMaterialInstanceParams(InstanceA, InstanceB, Alpha);
// Assign Nanite-compatible override material (UE5)
MyMID->SetNaniteOverride(NaniteCompatibleMaterial);
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.
// 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.
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
// 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.
Material Domain must be "Post Process". Add via:
PPV->AddOrUpdateBlendable(PostProcessMaterial, 1.0f); // weight 0.0–1.0
// 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".
#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:
| Format | Channels | Bits/Channel | Use Case |
|--------|----------|-------------|----------|
| RTF_RGBA8 | RGBA | 8 fixed | LDR color, UI |
| RTF_RGBA8_SRGB | RGBA | 8 fixed | sRGB color |
| RTF_RGBA16f | RGBA | 16 float | HDR color (default) |
| RTF_RGBA32f | RGBA | 32 float | High precision data |
| RTF_R16f | R | 16 float | Single channel data |
| RTF_RGB10A2 | RGB+A | 10+2 bit | Display output |
#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);
// 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*
);
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.
// 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);
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)
// 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);
Project Settings > Rendering > DBuffer Decals.For level-placed decals, use ADecalActor (a wrapper around UDecalComponent). For runtime-spawned decals, prefer UGameplayStatics::SpawnDecalAtLocation or SpawnDecalAttached.
Nanite is UE5's virtualized geometry system. Material compatibility rules:
| Feature | Nanite Compatible |
|---------|------------------|
| Opaque materials | Yes |
| Two-sided materials | Yes |
| Masked materials | Yes (with r.Nanite.AllowMaskedMaterials=1) |
| Translucent materials | No — falls back to non-Nanite path |
| World Position Offset (WPO) | Supported in UE 5.1+ (bEvaluateWorldPositionOffset on mesh) |
| Pixel Depth Offset | No |
| Custom vertex normals via shader | Limited |
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 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: 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.
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.
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 frame — ReadRenderTargetPixel 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.
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.
ue-cpp-foundations — UObject management, UPROPERTY, TObjectPtr, garbage collectionue-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 flagsnpx skills add quodsoler/ue-materials-rendering下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer