关于 ue-materials-rendering
面向 UE 5.8 C++ 材质与渲染开发的编码技能,按常见任务指引相关 API,并提供签名和实现说明,涵盖材质、渲染目标、场景捕获及渲染器功能。
主要功能
- 01运行时材质参数控制
- 02材质参数集合
- 03渲染目标与场景捕获
- 04后期处理与 Decal 指引
- 05UE 5.8 渲染器功能参考
适用场景
- 01修改单个 Actor 的材质值
- 02在多个材质间共享天气或时间值
- 03渲染小地图、镜面或摄像机画面
- 04实现后期处理效果或 Decal
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ quodsoler/ unreal-engine-skills --skill ue-materials-rendering安装后在 Claude Code 中输入/ue-materials-rendering确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/global-shaders-rdg.mdmaterial-parameter-reference.mdpost-process-settings.md详细介绍
UE 材质与渲染
目标引擎:UE 5.8。以下 API 已根据 5.8 头文件验证;较旧的形式列在“已弃用 — 请勿使用”下。
运行时材质与渲染 C++:UMaterialInstanceDynamic、UMaterialParameterCollection、UTextureRenderTarget2D、USceneCaptureComponent2D、FPostProcessSettings、UDecalComponent,以及 5.8 渲染器功能(Nanite、Lumen、MegaLights、Substrate、Virtual Shadow Maps、TSR)。材质头文件位于 Runtime/Engine/Public/Materials;渲染目标、场景和后期处理体积位于 Runtime/Engine/Classes/Engine;蓝图库位于 Runtime/Engine/Classes/Kismet。Build.cs:"Engine" 涵盖以上所有内容;只有在使用 EShaderPlatform、RDG 和全局着色器时,才添加 "RenderCore" 和 "RHI"。
上下文
如果 .agents/ue-project-context.md 存在,请阅读它(模块名称、约定、已启用插件、渲染器设置)。如果该文件缺失,请继续。
根据请求和代码库确定相关领域。只有当两种合理解读会产生不同代码时才提问。
| 请求涉及…… | 前往 |
|---|---|
| 在运行时更改网格上的材质参数 | 动态材质实例 |
| 一个值驱动多个材质(天气、一天中的时间) | 材质参数集合 |
| 小地图、镜子、监控摄像头、运行时纹理绘制 | 渲染目标 / 场景捕获 |
| Bloom、曝光、景深、色彩分级、Lumen 质量控制项 | 后期处理 |
| 以材质形式制作的全屏特效 | 后期处理材质 |
| 弹孔、血迹、投影纹理 | 贴花 |
| Nanite 网格/材质兼容性、WPO、Nanite 覆盖材质 | Nanite |
| GI、反射、多个带阴影的灯光 | Lumen 和 MegaLights |
| 分层/多 BSDF 材质系统 | Substrate |
| 轮廓、X 射线、模板遮罩、阴影/抗锯齿设置 | 阴影、抗锯齿和自定义深度 |
API 需要 EShaderPlatform,但你只有一个功能级别 | 功能级别与着色器平台 |
| 使用 C++ 编写实际的 HLSL 着色器通道 | 全局着色器与渲染图 |
完整的签名表位于 material-parameter-reference.md;FPostProcessSettings 字段表位于 post-process-settings.md;全局着色器和 RDG 代码位于 global-shaders-rdg.md。
动态材质实例
MID 是在父材质基础上按实例覆盖参数的对象。创建方式有三种:
| 需求 | 调用 |
|---|---|
| 创建未绑定到插槽的独立 MID | UMaterialInstanceDynamic::Create(Parent, Outer, Name) |
| 将 MID 分配给组件的材质插槽 | UPrimitiveComponent::CreateDynamicMaterialInstance(ElementIndex, SourceMaterial, OptionalName) |
| 通过面向 Blueprint 的库代码创建 MID | UKismetMaterialLibrary::CreateDynamicMaterialInstance(WorldContextObject, Parent, OptionalName, CreationFlags) |
原样签名:
// Materials/MaterialInstanceDynamic.h
static UMaterialInstanceDynamic* Create(class UMaterialInterface* ParentMaterial, class UObject* InOuter, FName Name = NAME_None);
// Components/PrimitiveComponent.h
virtual class UMaterialInstanceDynamic* CreateDynamicMaterialInstance(int32 ElementIndex, class UMaterialInterface* SourceMaterial = NULL, FName OptionalName = NAME_None);
// Kismet/KismetMaterialLibrary.h
static class UMaterialInstanceDynamic* CreateDynamicMaterialInstance(UObject* WorldContextObject, class UMaterialInterface* Parent, FName OptionalName = NAME_None, EMIDCreationFlags CreationFlags = EMIDCreationFlags::None);
创建一次,缓存到 UPROPERTY 中,并在 Tick 中或事件触发时更新:
// MyGlowActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyGlowActor.generated.h"
class UMaterialInstanceDynamic;
class UStaticMeshComponent;
UCLASS()
class MYGAME_API AMyGlowActor : public AActor
{
GENERATED_BODY()
public:
AMyGlowActor();
virtual void BeginPlay() override;
protected:
UPROPERTY(VisibleAnywhere, Category = "Rendering")
TObjectPtr<UStaticMeshComponent> Mesh;
UPROPERTY(Transient)
TObjectPtr<UMaterialInstanceDynamic> GlowMID;
};
// MyGlowActor.cpp
#include "MyGlowActor.h"
#include "Components/StaticMeshComponent.h"
#include "Materials/MaterialInstanceDynamic.h"
AMyGlowActor::AMyGlowActor()
{
Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
SetRootComponent(Mesh);
}
void AMyGlowActor::BeginPlay()
{
Super::BeginPlay();
GlowMID = Mesh->CreateDynamicMaterialInstance(0);
if (GlowMID)
{
GlowMID->SetVectorParameterValue(TEXT("GlowColor"), FLinearColor(1.f, 0.4f, 0.1f, 1.f));
GlowMID->SetScalarParameterValue(TEXT("GlowStrength"), 1.f);
}
}
如果每帧要更改数十个参数,请将名称查找替换为索引 API:先调用一次 InitializeScalarParameterAndGetIndex,之后每帧调用 SetScalarParameterByIndex。签名和对应的向量版本见 material-parameter-reference.md。
Setter(MaterialInstanceDynamic.h)——名称是区分大小写的 FName,必须与材质参数完全匹配:
void SetScalarParameterValue(FName ParameterName, float Value);
void SetVectorParameterValue(FName ParameterName, FLinearColor Value);
void SetDoubleVectorParameterValue(FName ParameterName, FVector4 Value);
void SetTextureParameterValue(FName ParameterName, class UTexture* Value);
void SetRuntimeVirtualTextureParameterValue(FName ParameterName, class URuntimeVirtualTexture* Value);
void SetSparseVolumeTextureParameterValue(FName ParameterName, class USparseVolumeTexture* Value);
void SetTextureCollectionParameterValue(FName ParameterName, UTextureCollection* Value);
除双精度向量和稀疏体积 setter 外,其他 setter 都有对应的 ByInfo 版本——例如 SetScalarParameterValueByInfo(const FMaterialParameterInfo& ParameterInfo, float Value)——可用于访问材质层或函数中的参数。
读取值:K2_GetScalarParameterValue(FName)、K2_GetVectorParameterValue(FName)、K2_GetTextureParameterValue(FName) 会直接返回值。UMaterialInterface 上的类型化形式是 bool GetScalarParameterValue(const FHashedMaterialParameterInfo& ParameterInfo, float& OutValue, bool bOveriddenOnly = false) const——它接受参数 info,而非单独的名称。
在实例之间复制:CopyParameterOverrides(仅复制已覆盖的参数)、CopyInterpParameters(仅复制源实例自身的标量、向量、纹理和字体覆盖,不遍历层级)、CopyMaterialUniformParameters(复制 uniform,跳过静态参数)、K2_CopyMaterialInstanceParameters(复制所有非静态参数)、CopyScalarAndVectorParameters(const UMaterialInterface&, EShaderPlatform) 和 K2_InterpolateMaterialInstanceParams(SourceA, SourceB, Alpha)。ClearParameterValues() 会清除所有覆盖值。完整签名见 material-parameter-reference.md。
静态参数(静态开关、静态布尔值、静态分量蒙版)无法在 MID 上更改。请在编辑器中为每种排列创建一个 UMaterialInstanceConstant,再在其上叠加 MID。
材质参数集合
UMaterialParameterCollection 是由 ScalarParameters / VectorParameters 数组组成的资源;每个引用它的材质都会从每个世界的一个 uniform buffer 中读取数据。通过 UWorld 获取对应世界的实例:
#include "Materials/MaterialParameterCollection.h"
#include "Materials/MaterialParameterCollectionInstance.h"
void AMyWeatherActor::SetRain(float Intensity)
{
UMaterialParameterCollectionInstance* Inst = GetWorld()->GetParameterCollectionInstance(RainCollection);
if (Inst)
{
Inst->SetScalarParameterValue(TEXT("RainIntensity"), Intensity);
Inst->SetVectorParameterValue(TEXT("RainTint"), FLinearColor(0.6f, 0.7f, 0.9f, 1.f));
}
}
如果集合中不存在该名称,两个 setter 都会返回 false。Getter 为 bool GetScalarParameterValue(FName ParameterName, float& OutParameterValue) const 及对应的向量版本。ForceReturnToDefaultValues() 会重置该实例。
面向 Blueprint 的库会为你执行世界查找(Kismet/KismetMaterialLibrary.h):
static void SetScalarParameterValue(UObject* WorldContextObject, UMaterialParameterCollection* Collection, FName ParameterName, float ParameterValue);
static void SetVectorParameterValue(UObject* WorldContextObject, UMaterialParameterCollection* Collection, FName ParameterName, const FLinearColor& ParameterValue);
static float GetScalarParameterValue(UObject* WorldContextObject, UMaterialParameterCollection* Collection, FName ParameterName);
static FLinearColor GetVectorParameterValue(UObject* WorldContextObject, UMaterialParameterCollection* Collection, FName ParameterName);
集合不包含纹理参数。若一个值会被多个材质读取,请使用集合;若要表示每个 Actor 的状态,请使用 MID。
渲染目标
UKismetRenderingLibrary(Kismet/KismetRenderingLibrary.h)负责运行时渲染目标工作流:
static UTextureRenderTarget2D* CreateRenderTarget2D(UObject* WorldContextObject, int32 Width = 256, int32 Height = 256, ETextureRenderTargetFormat Format = RTF_RGBA16f, FLinearColor ClearColor = FLinearColor::Black, bool bAutoGenerateMipMaps = false, bool bSupportUAVs = false);
static void ClearRenderTarget2D(UObject* WorldContextObject, UTextureRenderTarget2D* TextureRenderTarget, FLinearColor ClearColor = FLinearColor(0, 0, 0, 1));
static void ResizeRenderTarget2D(UTextureRenderTarget2D* TextureRenderTarget, int32 Width = 256, int32 Height = 256);
static void ReleaseRenderTarget2D(UTextureRenderTarget2D* TextureRenderTarget);
static void DrawMaterialToRenderTarget(UObject* WorldContextObject, UTextureRenderTarget2D* TextureRenderTarget, UMaterialInterface* Material);
static void ExportRenderTarget(UObject* WorldContextObject, UTextureRenderTarget2D* TextureRenderTarget, const FString& FilePath, const FString& FileName);
static FColor ReadRenderTargetPixel(UObject* WorldContextObject, UTextureRenderTarget2D* TextureRenderTarget, int32 X, int32 Y);
ETextureRenderTargetFormat(Engine/TextureRenderTarget2D.h):RTF_R8、RTF_RG8、RTF_RGBA8、RTF_RGBA8_SRGB、RTF_R16f、RTF_RG16f、RTF_RGBA16f、RTF_R32f、RTF_RG32f、RTF_RGBA32f、RTF_RGB10A2。选择能够容纳数据的最小格式——LDR/UI 使用 RTF_RGBA8,HDR 使用 RTF_RGBA16f,单通道使用 RTF_R16f。
需要精确像素格式时,可手动创建:
UTextureRenderTarget2D* RT = NewObject<UTextureRenderTarget2D>(this);
RT->InitCustomFormat(512, 512, PF_FloatRGBA, /*bInForceLinearGamma=*/true);
RT->UpdateResourceImmediate(/*bClearRenderTarget=*/true);
Canvas 绘制会将多次绘制合并为一次渲染目标切换:
UCanvas* Canvas = nullptr;
FVector2D CanvasSize = FVector2D::ZeroVector;
FDrawToRenderTargetContext Context;
UKismetRenderingLibrary::BeginDrawCanvasToRenderTarget(this, RT, Canvas, CanvasSize, Context);
Canvas->K2_DrawMaterial(IconMaterial, FVector2D::ZeroVector, CanvasSize, FVector2D::ZeroVector);
UKismetRenderingLibrary::EndDrawCanvasToRenderTarget(this, Context);
UCanvasRenderTarget2D(Engine/CanvasRenderTarget2D.h)封装了该循环:使用 CreateCanvasRenderTarget2D(WorldContextObject, CanvasRenderTarget2DClass, Width, Height) 创建,绑定 OnCanvasRenderTargetUpdate,并在需要重绘内容时调用 UpdateResource()。
ReadRenderTargetPixel 等函数会导致 GPU 停顿。请在编辑器工具或一次性捕获中使用,切勿每帧调用;如需流式读取,请使用 FRHIGPUTextureReadback。
场景捕获
USceneCaptureComponent2D 会将世界渲染到 UTextureRenderTarget2D 中。它自身的字段包括:TextureTarget、FOVAngle、PostProcessSettings、PostProcessBlendWeight、bMainViewResolution、CaptureScene()、CaptureSceneDeferred()。从 USceneCaptureComponent 继承的成员包括:CaptureSource、bCaptureEveryFrame、bCaptureOnMovement、PrimitiveRenderMode、ShowOnlyActors、HiddenActors、ShowFlags、ShowOnlyComponent()、ShowOnlyActorComponents()。
#include "Components/SceneCaptureComponent2D.h"
SceneCapture = CreateDefaultSubobject<USceneCaptureComponent2D>(TEXT("SceneCapture"));
SceneCapture->SetupAttachment(RootComponent);
SceneCapture->FOVAngle = 90.f;
SceneCapture->CaptureSource = ESceneCaptureSource::SCS_FinalColorLDR;
SceneCapture->bCaptureEveryFrame = false; // drive it manually
SceneCapture->bCaptureOnMovement = false;
SceneCapture->PrimitiveRenderMode = ESceneCapturePrimitiveRenderMode::PRM_UseShowOnlyList;
SceneCapture->ShowFlags.SetFog(false);
SceneCapture->ShowFlags.SetMotionBlur(false);
ESceneCaptureSource(Engine/EngineTypes.h):SCS_SceneColorHDR、SCS_SceneColorHDRNoAlpha、SCS_FinalColorLDR、SCS_SceneColorSceneDepth、SCS_SceneDepth、SCS_DeviceDepth、SCS_Normal、SCS_BaseColor、SCS_FinalColorHDR、SCS_FinalToneCurveHDR。SCS_Normal 和 SCS_BaseColor 仅适用于延迟渲染器。
将 bCaptureEveryFrame = false 和 bCaptureOnMovement = false 设置为 false 后,在视图确实需要更新时调用 SceneCapture->CaptureScene()——场景捕获会额外完整渲染一次场景。
后期处理
FPostProcessSettings(Engine/Scene.h)由 APostProcessVolume::Settings、UPostProcessComponent::Settings、UCameraComponent::PostProcessSettings 和 USceneCaptureComponent2D::PostProcessSettings 持有。每个值字段都有对应的 bOverride_<Field> 位;除非该覆盖位为 true,否则该值会被忽略。
#include "Engine/PostProcessVolume.h"
#include "Engine/Scene.h"
void AMyMoodDirector::ApplyDangerMood(APostProcessVolume* Volume)
{
Volume->bEnabled = true;
Volume->bUnbound = true;
Volume->Priority = 10.f;
Volume->BlendWeight = 1.f;
FPostProcessSettings& S = Volume->Settings;
S.bOverride_BloomIntensity = true;
S.BloomIntensity = 0.35f;
S.bOverride_VignetteIntensity = true;
S.VignetteIntensity = 0.8f;
S.bOverride_ColorSaturation = true;
S.ColorSaturation = FVector4(0.6f, 0.6f, 0.6f, 1.f);
}
APostProcessVolume 和 UPostProcessComponent 都公开了 Settings、Priority、BlendRadius、BlendWeight、bEnabled、bUnbound 以及 AddOrUpdateBlendable(TScriptInterface<IBlendableInterface> InBlendableObject, float InWeight = 1.0f)。重叠的体积中,Priority 较高者优先;bUnbound 会使体积在任何位置生效。
按摄像机设置后期处理:使用 UCameraComponent::PostProcessSettings 和 PostProcessBlendWeight,以及 AddOrUpdateBlendable / RemoveBlendable。(AddExtraPostProcessBlend 只会存储设置——只有编辑器视口会读取这些设置,Camera/CameraComponent.h:323。)在摄像机修饰器或摄像机管理器中,通过 APlayerCameraManager::AddCachedPPBlend(FPostProcessSettings& PPSettings, float BlendWeight, EViewTargetBlendOrder BlendOrder = VTBlendOrder_Base) 推送临时混合;使用 GetCachedPostProcessBlends 读取。缓存会在每帧开始时的 ApplyCameraModifiers 中清空(PlayerCameraManager.cpp:289),因此每帧都要重新添加混合;ClearCachedPPBlends 和 PostProcessBlendCache 数组都受保护(Camera/PlayerCameraManager.h:371,391)。
有关 bloom、exposure、DOF、color grading、film grain、AO、Lumen、SSR、motion blur 和 lens 的逐字段表格,请参阅 post-process-settings.md。
后期处理材质
后期处理材质是 MaterialDomain 为 MD_PostProcess 的 UMaterial(MaterialDomain.h)。相关 UMaterial 字段:BlendableLocation、BlendablePriority、bIsBlendable、UserSceneTexture。
EBlendableLocation(Engine/BlendableInterface.h):BL_SceneColorBeforeDOF、BL_SceneColorAfterDOF、BL_TranslucencyAfterDOF、BL_SSRInput、BL_SceneColorBeforeBloom、BL_ReplacingTonemapper、BL_SceneColorAfterTonemapping。
// Weight 0 = invisible, 1 = full. Calling it again with the same object updates the weight.
Volume->AddOrUpdateBlendable(OutlineMaterial, 1.0f);
// A MID works as a blendable too, so the effect can be parameterised at runtime.
UMaterialInstanceDynamic* OutlineMID = UMaterialInstanceDynamic::Create(OutlineMaterial, this);
OutlineMID->SetScalarParameterValue(TEXT("Thickness"), 2.f);
Volume->AddOrUpdateBlendable(OutlineMID, 1.0f);
如果持有的是设置结构体而非体积,FPostProcessSettings::AddBlendable(TScriptInterface<IBlendableInterface> InBlendableObject, float InWeight) 和 RemoveBlendable 会直接操作 WeightedBlendables 数组。
Decal
UDecalComponent(Components/DecalComponent.h)会将材质投射到其盒体覆盖的对象上:
void SetDecalMaterial(class UMaterialInterface* NewDecalMaterial);
class UMaterialInterface* GetDecalMaterial() const;
virtual class UMaterialInstanceDynamic* CreateDynamicMaterialInstance();
void SetFadeOut(float StartDelay, float Duration, bool DestroyOwnerAfterFade = true);
void SetFadeIn(float StartDelay, float Duration);
void SetFadeScreenSize(float NewFadeScreenSize);
void SetSortOrder(int32 Value);
void SetDecalColor(const FLinearColor& Color);
字段:DecalSize(局部空间范围,用于缩放投射盒)、SortOrder、FadeScreenSize、FadeStartDelay、FadeDuration、DecalColor。
通过 UGameplayStatics(Kismet/GameplayStatics.h)在运行时生成:
UDecalComponent* Decal = UGameplayStatics::SpawnDecalAtLocation(
this, BulletHoleMaterial, FVector(16.f, 16.f, 16.f), Hit.ImpactPoint, Hit.ImpactNormal.Rotation(), 10.f);
if (Decal)
{
UMaterialInstanceDynamic* DecalMID = Decal->CreateDynamicMaterialInstance();
DecalMID->SetScalarParameterValue(TEXT("Wear"), 0.25f);
}
SpawnDecalAttached(DecalMaterial, DecalSize, AttachToComponent, AttachPointName, Location, Rotation, LocationType, LifeSpan) 会跟随移动组件。LifeSpan = 0 表示 Decal 会一直存在。之后若要更改,可调用 UDecalComponent::SetLifeSpan(float)(Components/DecalComponent.h:153,不是 UFUNCTION),或通过 SetFadeOut 控制淡出。
DBuffer Decal 会在基础通道之前累积到缓冲区,因此能够正确影响光照贴图和天空光照;它们需要启用项目设置 r.DBuffer(URendererSettings::bDBuffer,Engine/RendererSettings.h:942),且更改后需要重启。未启用时,常规延迟渲染 Decal 仍会写入 GBuffer,但无法正确影响烘焙光照或天空光照。
Nanite
UStaticMesh::HasValidNaniteData() 用于报告网格是否具有 Nanite 渲染数据(IsNaniteEnabled() 属于 WITH_EDITORONLY_DATA,仅限编辑器构建,Engine/StaticMesh.h:1049)。设置应通过访问器而非原始字段进行:GetNaniteSettings() / SetNaniteSettings(const FMeshNaniteSettings&) / NotifyNaniteSettingsChanged()。
const UStaticMesh* Mesh = MeshComponent->GetStaticMesh();
const bool bIsNanite = Mesh && Mesh->HasValidNaniteData();
编写代码前值得了解的材质规则:
| 材质属性 | Nanite |
|---|---|
| 不透明、双面 | 支持 |
| 遮罩 | 支持;受 r.Nanite.AllowMaskedMaterials 控制 |
| 世界位置偏移 | 支持;可在 UStaticMeshComponent 上逐组件调用 SetEvaluateWorldPositionOffset(bool) 和 SetWorldPositionOffsetDisableDistance(int32) |
| 半透明 | Nanite 不会渲染;网格会回退 |
| 材质使用标记 | MATUSAGE_Nanite;蒙皮 Nanite 还需要 MATUSAGE_SkeletalMesh(NaniteResources.cpp:2648) |
材质可以通过 FMaterialOverrideNanite 携带 Nanite 专用替换材质(Materials/MaterialOverrideNanite.h,GetOverrideMaterial() / SetOverrideMaterial(UMaterialInterface*, bool));对于 MID,对应接口是 SetNaniteOverride(UMaterialInterface* InMaterial)。
Nanite 装配体会用多个部分组合成一个 Nanite 网格:FNaniteAssemblyData、FNaniteAssemblyPart、FNaniteAssemblyNode、FNaniteAssemblyBoneInfluence(Engine/NaniteAssemblyData.h)。可通过组件上的 USkinnedMeshComponent::HasValidNaniteData() / GetNaniteResources() 查询蒙皮 Nanite,并通过逐组件设置 bDisallowNanite 将其禁用。
Lumen 和 MegaLights
Lumen 的质量由 FPostProcessSettings 控制:LumenSceneDetail、LumenSceneLightingQuality、LumenSceneLightingUpdateSpeed、LumenFinalGatherQuality、LumenFinalGatherLightingUpdateSpeed、LumenMaxTraceDistance、LumenReflectionQuality、LumenRayLightingMode——每项都有对应的 bOverride_* 位。ELumenRayLightingModeOverride 的取值包括 Default、SurfaceCache、HitLightingForReflections、HitLighting。渲染器路径本身由同一结构体中的 DynamicGlobalIlluminationMethod 和 ReflectionMethod 选择。
与逐体积覆盖相比,调整可伸缩性设置成本更低:sg.GlobalIlluminationQuality 会选择 BaseScalability.ini 中的 [GlobalIlluminationQuality@N] 配置块。等级 1(“Lumen Lite”)会将 Lumen 切换为使用辐照度体积进行最终采集,并通过 r.Lumen.FinalGatherMethod=0 使用精简的表面缓存;等级 2 及以上使用屏幕探针采集。在 C++ 中,可使用 Scalability.h 中的 Scalability::SetQualityLevels(const FQualityLevels& QualityLevels, bool bForce = false)。
MegaLights 使用随机采样渲染多个投射阴影的光源。项目范围内的设置是 URendererSettings::bEnableMegaLights(控制台变量 r.MegaLights.EnableForProject);逐体积设置是 FPostProcessSettings::bMegaLights,并使用 bOverride_bMegaLights。运行时 CVar 包括 r.MegaLights.Allowed 和 r.MegaLights.HardwareRayTracing。它需要光线追踪数据——硬件光线追踪,或作为回退方案的软件(Lumen)追踪(MegaLights::HasRequiredTracingData、Renderer/Private/MegaLights/MegaLights.cpp:541)——会取代它所处理光源的其他直接光照和阴影路径;除非设置 r.MegaLights.DirectionalLights=1,否则会跳过方向光(默认值为 0,MegaLights.cpp:239)。
FPostProcessSettings& S = Volume->Settings;
S.bOverride_bMegaLights = true;
S.bMegaLights = true;
Substrate
Substrate 是分层材质系统,可通过 URendererSettings::bEnableSubstrate(r.Substrate)按项目启用;r.Substrate.ProjectGBufferFormat 和 r.Substrate.ProjectClosuresPerPixel 用于控制内存和闭包预算。Substrate 材质节点位于 Materials/MaterialExpressionSubstrate.h。
自定义 Substrate 表达式会构建拓扑树;5.8 版要重载的虚函数(仅限编辑器,在 Materials/MaterialExpression.h:481 的 #if WITH_EDITOR 中)是:
virtual FSubstrateOperator* SubstrateGenerateMaterialTopologyTree(struct FSubstrateTranslatorDataInterface& SubstrateTranslatorData, class UMaterialExpression* Parent, int32 OutputIndex) override;
静态辅助函数是 SubstrateGenerateMaterialTopologyTreeCommon,它也会首先接收 FSubstrateTranslatorDataInterface&。旧版接收 FMaterialCompiler* 的重载已弃用。
阴影、抗锯齿和自定义深度
虚拟阴影贴图是默认的阴影路径;r.Shadow.Virtual.Enable 用于启用它们。使用 World Position Offset 的材质必须在组件上启用 WPO 评估(SetEvaluateWorldPositionOffset),才能使阴影匹配。
抗锯齿设置为 URendererSettings::DefaultFeatureAntiAliasing(EAntiAliasingMethod,SceneUtils.h);AAM_TSR 表示时间超分辨率。运行时覆盖项为 r.AntiAliasingMethod。
自定义深度/模板会为轮廓、X 射线和高亮后处理材质提供数据。项目设置:URendererSettings::CustomDepthStencil(ECustomDepthStencil),控制台变量 r.CustomDepth。逐组件设置:
MeshComponent->SetRenderCustomDepth(true);
MeshComponent->SetCustomDepthStencilValue(1); // 0-255, read as CustomStencil in a post-process material
功能级别和着色器平台
材质查询 API 接收的是 EShaderPlatform(RHIShaderPlatform.h),而不是 ERHIFeatureLevel::Type(RHIFeatureLevel.h)。使用 RHIGlobals.h 中的 GetFeatureLevelShaderPlatform 进行转换:
#include "RHIGlobals.h"
const ERHIFeatureLevel::Type FeatureLevel = GetWorld()->GetFeatureLevel();
const EShaderPlatform ShaderPlatform = GetFeatureLevelShaderPlatform(FeatureLevel);
if (MyMaterial->IsCompilingOrHadCompileError(ShaderPlatform)) // UMaterial* -- not on UMaterialInterface
{
// hide the object until its shaders are ready
}
使用标志通过 UMaterialInterface 上的 GetUsageByFlag(EMaterialUsage Usage) 读取,切勿直接访问 bUsedWith* 字段:
if (!MyMaterial->GetUsageByFlag(MATUSAGE_Nanite))
{
// not authorised for Nanite meshes
}
CheckMaterialUsage(EMaterialUsage) 和 CheckMaterialUsage_Concurrent(EMaterialUsage) const 会设置标志,并在标志缺失时触发重新编译;SetMaterialUsage(EMaterialUsage Usage) 是虚函数。
全局着色器与渲染图
对于真正的 HLSL 通道,声明一个 FGlobalShader 子类,并使用 DECLARE_GLOBAL_SHADER、SHADER_USE_PARAMETER_STRUCT 和 BEGIN_SHADER_PARAMETER_STRUCT 块;使用 IMPLEMENT_GLOBAL_SHADER 将其绑定到 .usf,并通过 GraphBuilder.AddPass(...) 向 FRDGBuilder 添加通道,然后调用 GraphBuilder.Execute() 提交。Build.cs 需要 "RenderCore"、"RHI" 和 "Projects"。
两条规则会导致大多数故障。在 StartupModule 中使用 AddShaderSourceDirectoryMapping 注册着色器虚拟路径。为该模块设置 "LoadingPhase": "PostConfigInit":如果使用常见的 Default 阶段,全局着色器类型注册得太晚,会触发 "Shader type was loaded too late" checkf(Shader.cpp:315)。完整的计算着色器声明、AddPass 形式和模块启动代码:references/global-shaders-rdg.md。
已弃用 — 请勿使用
| 请勿输出 | 在 5.8 中使用 | 来源 |
|---|---|---|
Material->bUsedWithNanite(任何 bUsedWith* 字段) | Material->GetUsageByFlag(MATUSAGE_Nanite) | Materials/Material.h 中的 UE_DEPRECATED(5.8)(27 个标志) |
UMaterial::bEnableExecWire | 无 — 该实验已移除 | Materials/Material.h 中的 UE_DEPRECATED(5.8) |
SetMaterialUsage(bool& bNeedsRecompile, EMaterialUsage, UMaterialInterface*) | SetMaterialUsage(EMaterialUsage Usage) | Materials/Material.h 中的 UE_DEPRECATED(5.8) |
IsCompilingOrHadCompileError(ERHIFeatureLevel::Type) | IsCompilingOrHadCompileError(EShaderPlatform) | Materials/Material.h 中的 UE_DEPRECATED(5.7) |
GetMaterialResource(ERHIFeatureLevel::Type, ...) | GetMaterialResource(EShaderPlatform, ...) | Materials/MaterialInterface.h 中的 UE_DEPRECATED(5.7) |
GetUsedTexturesAndIndices(..., ERHIFeatureLevel::Type) | EShaderPlatform 重载 | Materials/MaterialInterface.h 中的 UE_DEPRECATED(5.7) |
CopyScalarAndVectorParameters(Source, ERHIFeatureLevel::Type) | CopyScalarAndVectorParameters(Source, EShaderPlatform) | Materials/MaterialInstanceDynamic.h 中的 UE_DEPRECATED(5.7) |
GetUsedMaterialPropertyDesc(ERHIFeatureLevel::Type) | GetUsedMaterialPropertyDesc(EShaderPlatform) | Components/PrimitiveComponent.h 中的 UE_DEPRECATED(5.7) |
UMaterialInstance::Resource(公共渲染代理) | GetRenderProxy() 或 GetInstanceRenderProxy() | Materials/MaterialInstance.h 中的 UE_DEPRECATED(5.8) |
UMaterialExpressionTextureBase::GetSamplerTypeForTexture | MaterialExpressionUtils::GetSamplerTypeForTexture | Materials/MaterialExpressionTextureBase.h 中的 UE_DEPRECATED(5.8) |
UMaterialExpressionTextureBase::VerifySamplerType | MaterialExpressionUtils::VerifySamplerType | Materials/MaterialExpressionTextureBase.h 中的 UE_DEPRECATED(5.8) |
SubstrateGenerateMaterialTopologyTreeCommon(FMaterialCompiler*, ...) | FSubstrateTranslatorDataInterface& 重载 | Materials/MaterialExpressionSubstrate.h 中的 UE_DEPRECATED(5.8) |
FTextureSamplingInfo CalculateTexturesSamplingInfo(UTexture*) | 带输出参数的 const bool 形式 | Materials/MaterialInterface.h 中的 UE_DEPRECATED(5.8) |
IsUsingNewTranslatorPrototype() | IsUsingNewHLSLGenerator() | Materials/MaterialInterface.h 中的 UE_DEPRECATED(5.8) |
CreateMIDForElement / CreateMIDForElementFromMaterial | CreateDynamicMaterialInstance(ElementIndex, SourceMaterial, OptionalName) | Components/PrimitiveComponent.h 中的 DeprecatedFunction |
StaticMesh->NaniteSettings(直接访问字段) | GetNaniteSettings() / SetNaniteSettings() | Engine/StaticMesh.h 中的 UE_DEPRECATED(5.7) |
BL_BeforeTranslucency、BL_BeforeTonemapping、BL_AfterTonemapping | BL_SceneColorBeforeDOF、BL_SceneColorAfterDOF、BL_SceneColorAfterTonemapping | Engine/BlendableInterface.h 中的 UE_DEPRECATED(5.4) |
常见错误
每帧都创建 MID: 每次调用 CreateDynamicMaterialInstance 都会分配一个新实例和渲染代理。在 BeginPlay 中创建一次并保存,之后只调用设置函数。
在裸指针中保存 MID: MID 是 UObject,只要没有任何引用指向它,就会被垃圾回收。将它保存在 UPROPERTY() TObjectPtr<UMaterialInstanceDynamic> 中,绝不要将其作为裸 UMaterialInstanceDynamic* 成员或静态变量保存。
设置后处理值时未设置对应的覆盖标记: 单独执行 S.BloomIntensity = 2.f; 不会产生任何效果。每次赋值都需要在旁边设置 S.bOverride_BloomIntensity = true;。
需要着色器平台时却传入功能级别: GetMaterialResource、IsCompilingOrHadCompileError、CopyScalarAndVectorParameters 和 GetUsedMaterialPropertyDesc 中接受 ERHIFeatureLevel::Type 的重载已弃用。使用 GetFeatureLevelShaderPlatform(GetWorld()->GetFeatureLevel()) 转换一次即可。
读取 bUsedWith* 字段: 这些成员已弃用。GetUsageByFlag(EMaterialUsage) 是唯一受支持的读取方式,SetMaterialUsage(EMaterialUsage) 是唯一受支持的写入方式。
以为 SetVectorParameterValue 支持双精度: FVector/FVector4 重载是内联便捷函数,会将值转换为 FLinearColor(浮点型)(Materials/MaterialInstanceDynamic.h:112-113)。当材质读取双精度向量时,请使用 SetDoubleVectorParameterValue(FName, FVector4)。
复制 MID: MID 是客户端本地对象。复制标量/向量值,并在每个客户端的 OnRep 中重建 MID 的状态。
以为静态开关可以在运行时更改: 静态参数会编译进着色器排列。为每种排列创建一个 UMaterialInstanceConstant,并将 MID 的父级设为正确的实例。
在 Tick 中调用 ReadRenderTargetPixel: 它会刷新渲染命令并阻塞管线。仅在编辑器工具中进行读回,或使用 FRHIGPUTextureReadback。
让 bCaptureEveryFrame 保持开启: 每帧都捕获的 USceneCaptureComponent2D 会再次渲染场景。关闭此选项,并在目标实际发生变化时调用 CaptureScene()。
相关技能
ue-niagara-effects— Niagara 系统、发射器参数,以及粒子所需的材质使用标记ue-procedural-generation— 为这些材质赋值的 PCG 和运行时网格生成ue-actor-component-architecture— 此处所用组件的设置、附加和生命周期ue-ui-umg-slate—MD_UI材质、控件画笔,以及将 UMG 渲染到渲染目标ue-sequencer-cinematics— 从序列中为材质参数和后处理设置制作动画ue-testing-debugging— 性能分析、stat命令、Insights 和 GPU 捕获工作流ue-data-assets-tables— 驱动材质和渲染配置的数据资产与表格ue-cpp-foundations—UObject生命周期、UPROPERTY、TObjectPtr和垃圾回收