$npx -y skills add quodsoler/unreal-engine-skills --skill ue-materials-renderingUse 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 referenc
| 1 | # UE Materials and Rendering |
| 2 | |
| 3 | 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). |
| 4 | |
| 5 | --- |
| 6 | |
| 7 | ## Step 1: Read Project Context |
| 8 | |
| 9 | Read `.agents/ue-project-context.md` before giving advice. From it, extract: |
| 10 | |
| 11 | - **Engine version** — UE5.0–5.4 APIs differ (e.g., `SetNaniteOverride` added in 5.x; `CopyScalarAndVectorParameters` signature changed in 5.7) |
| 12 | - **Target platforms** — Mobile requires forward rendering; many post-process features are desktop-only |
| 13 | - **Rendering settings** — Nanite/Lumen enabled status affects which material features are safe |
| 14 | - **Module names** — needed for correct `#include` paths and `Build.cs` dependencies |
| 15 | |
| 16 | If the context file is missing, ask for engine version and target platforms before proceeding. |
| 17 | |
| 18 | --- |
| 19 | |
| 20 | ## Step 2: Clarify the Rendering Need |
| 21 | |
| 22 | Ask which area the user needs: |
| 23 | |
| 24 | 1. **Dynamic Material Instances (MID)** — runtime parameter changes on mesh components |
| 25 | 2. **Material Parameter Collections** — global parameters shared across all materials |
| 26 | 3. **Post-Process** — bloom, exposure, color grading, DOF, AO via volumes or components |
| 27 | 4. **Render Targets** — scene capture, minimap, security camera, canvas drawing |
| 28 | 5. **Decals** — deferred decals spawned at runtime, fade, sort order |
| 29 | 6. **Rendering Pipeline / UE5 Features** — Nanite, Lumen, Virtual Shadow Maps, custom depth/stencil |
| 30 | |
| 31 | Multiple areas can be combined. |
| 32 | |
| 33 | --- |
| 34 | |
| 35 | ## Core Patterns |
| 36 | |
| 37 | ### 1. Dynamic Material Instances (MID) |
| 38 | |
| 39 | #### Creation |
| 40 | |
| 41 | **Pattern A — from UMaterialInterface (standalone, not tied to a component slot):** |
| 42 | |
| 43 | ```cpp |
| 44 | // Header |
| 45 | UPROPERTY() |
| 46 | TObjectPtr<UMaterialInstanceDynamic> MyMID; |
| 47 | |
| 48 | // Implementation — call once (BeginPlay or equivalent), cache the result |
| 49 | UMaterialInterface* BaseMat = LoadObject<UMaterialInterface>( |
| 50 | nullptr, TEXT("/Game/Materials/M_MyBase.M_MyBase")); |
| 51 | |
| 52 | MyMID = UMaterialInstanceDynamic::Create(BaseMat, this); |
| 53 | ``` |
| 54 | |
| 55 | **Pattern B — via component slot (preferred for meshes):** |
| 56 | |
| 57 | ```cpp |
| 58 | // UMeshComponent::CreateDynamicMaterialInstance creates a MID for the given |
| 59 | // element index and assigns it to the slot automatically. |
| 60 | // Signature: CreateDynamicMaterialInstance(int32 ElementIndex, |
| 61 | // UMaterialInterface* SourceMaterial = nullptr, |
| 62 | // FName OptionalName = NAME_None) |
| 63 | |
| 64 | UMaterialInstanceDynamic* MID = MeshComponent->CreateDynamicMaterialInstance( |
| 65 | 0, // element index |
| 66 | nullptr, // nullptr = use the slot's current material as parent |
| 67 | TEXT("MyMID") // optional debug name |
| 68 | ); |
| 69 | ``` |
| 70 | |
| 71 | Source: `MaterialInstanceDynamic.h`, `PrimitiveComponent.h`. Build.cs: `"Engine"`. |
| 72 | |
| 73 | #### Setting Parameters |
| 74 | |
| 75 | ```cpp |
| 76 | MyMID->SetScalarParameterValue(TEXT("Opacity"), 0.5f); |
| 77 | MyMID->SetVectorParameterValue(TEXT("BaseColor"), FLinearColor(1.f, 0.2f, 0.1f, 1.f)); |
| 78 | MyMID->SetVectorParameterValue(TEXT("Offset"), FLinearColor(0.f, 0.f, 100.f, 0.f)); // XYZ via FLinearColor |
| 79 | MyMID->SetTextureParameterValue(TEXT("DamageMask"), MyTexture); |
| 80 | MyMID->SetTextureParameterValue(TEXT("SecurityFeed"), RenderTargetAsset); // RT as texture |
| 81 | ``` |
| 82 | |
| 83 | Full setter signatures from `MaterialInstanceDynamic.h`: |
| 84 | ```cpp |
| 85 | void SetScalarParameterValue(FName ParameterName, float Value); |
| 86 | void SetVectorParameterValue(FName ParameterName, FLinearColor Value); // Pass FLinearColor; no implicit conversion from FVector |
| 87 | void SetTextureParameterValue(FName ParameterName, UTexture* Value); |
| 88 | ``` |
| 89 | |
| 90 | #### High-Frequency Updates — Index-Based API |
| 91 | |
| 92 | When setting dozens of parameters per frame (rare but valid), use index caching: |
| 93 | |
| 94 | ```cpp |
| 95 | // In BeginPlay or initialization — call once per parameter name: |
| 96 | int32 OpacityIndex = -1; |
| 97 | MyMID->InitializeScalarParameterAndGetIndex(TEXT("Opacity"), 1.0f, OpacityIndex); |
| 98 | |
| 99 | // In Tick — use index, no name lookup: |
| 100 | if (OpacityIndex >= 0) |
| 101 | { |
| 102 | MyMID->SetScalarParameterByIndex(OpacityIndex, NewOpacity); |
| 103 | } |
| 104 | ``` |
| 105 | |
| 106 | Index is invalidated if the parent material changes. Do not share indices across different MID instances. |
| 107 | |
| 108 | #### MID Lifecycle and GC |
| 109 | |
| 110 | MIDs are `UObject`s — they are garbage collected when unreferenced. To keep a MID alive: |
| 111 | |
| 112 | ```cpp |
| 113 | // In your class header — must be UPROPERTY to prevent GC |
| 114 | UPROPERTY() |
| 115 | TObjectPtr<UMaterialInstanceDynamic> CachedMID; |
| 116 | ``` |
| 117 | |
| 118 | Never store MIDs in raw pointers or local variables across frames. |
| 119 | |
| 120 | #### Additional MID Operations |
| 121 | |
| 122 | ```cpp |
| 123 | // Lerp between two instances' scalar/vector params |
| 124 | MyMID->K2_InterpolateMaterialInstanceParams(InstanceA, InstanceB |