--- title: 剖析虚幻渲染体系(09)- 材质体系 date: 2024-02-05 22:00:48 excerpt: tags: rating: ⭐ --- # 前言 https://www.cnblogs.com/timlly/p/15109132.html # 概念 - UMaterialInterface:材质的基础接口类, 定义了大量材质相关的数据和接口, 部分接口是空实现或未实现的接口。 - USubsurfaceProfile* SubsurfaceProfile:次表面散射轮廓,常用于皮肤、玉石等材质。 - TArray AssetUserData:用户数据,可以存多个数据。 - FLightmassMaterialInterfaceSettings LightmassSettings:离线GI数据。 - TArray TextureStreamingData:纹理流数据。 - UMaterial:材质类,继承自 UMaterialInterface,对应着一个材质资源文件。定义了材质所需的所有数据和操作接口,并负责打通其它关联类型的链接。 - TArray MaterialResources:材质渲染资源,一个材质可以拥有多个渲染资源实例。 - FDefaultMaterialInstance* DefaultMaterialInstance:默认的材质渲染代理,继承自FMaterialRenderProxy。 - UPhysicalMaterial* PhysMaterial:物理材质。 - TArray LoadedMaterialResources:已经加载的材质资源,通常由游戏线程从磁盘加载并序列化而来。 - 材质的各种属性和标记。 - UMaterialInstance:材质实例,**不能单独存在**,而**需要依赖UMaterialInterface类型的父类**,意味着父类可以是UMaterialInterface的任意一个子类,但最上层的父类必须是UMaterial。 - UMaterialInstanceConstant:为了**避免运行时因修改材质参数而引起重新编译**,它内部有限的数据覆盖也是因为此。如果不重新编译,就无法支持对材质的常规修改,因此实例只能更改预定义的材质参数的值。 这里的参数就是在材质编辑器内定义的唯一的名称、类型和默认值静态定义。另外,需要明确注意的是,在运行时的代码(非编辑器代码)中,我们是无法更改UMaterialInstanceConstant实例的材质属性。UMaterialInstanceConstant还有一个专用于渲染地貌的ULandscapeMaterialInstanceConstant的子类。 - UMaterialInstanceDynamic:提供了可以在**运行时代码动态创建和修改材质属性的功能**,并且同样**不会引起材质重新编译**。 - **FMaterialRenderProxy**:FMaterialRenderProxy负责**接收游戏线程代表的数据,然后传递给渲染器去处理和渲染**。FMaterialRenderProxy是个抽象类,定义了一个静态全局的材质渲染代理映射表和获取FMaterial渲染实例的接口。具体的逻辑由子类完成。 - **FDefaultMaterialInstance**:渲染UMaterial的默认代表实例。 - **FMaterialInstanceResource**:渲染UMaterialInstance实例的代表。 - FColoredMaterialRenderProxy:覆盖材质颜色向量参数的材质渲染代表。 - FLandscapeMaskMaterialRenderProxy:地貌遮罩材质渲染代表。 - FLightmassMaterialProxy:Lightmass材质渲染代理。 - ...... - **FMaterial**: - **描述材质的编译过程**,并提供可扩展性钩子(CompileProperty等) 。 - **将材质属性传递到渲染器**,并使用函数访问材质属性。 - 存储缓存的**ShaderMap**(GameThread & RenderingThread),和其他来自编译的瞬态输出,这对异步着色器编译是必要的。 - **FMaterialResource**:继承自FMaterial,实现**材质接口**。 - 拥有对应的UMaterial、UMaterialInstance指针。 ![[UE_UMaterial.png]] UE的材质为何会有如此多的概念和类型,它们的关系到底怎么样?本节尝试阐述它们的关联和作用。 首先阐述**UMaterialInterface和它的子类们,它们是引擎模块在游戏线程的代表**。UMaterialInterface继承UOjbect,提供了材质的抽象接口,为子类提供了一致的行为和规范,也好统一不同类型的子类之间的差异。子类UMaterial则对应着用材质编辑器生成的材质蓝图的资源,保存了各种表达式节点及各种参数。另一个子类UMaterialInstance则抽象了材质实例的接口,是为了支持修改材质参数后不引发材质重新编译而存在的,同时统一和规范固定实例(UMaterialInstanceConstant)和动态实例(UMaterialInstanceDynamic)两种子类的数据和行为。UMaterialInstanceConstant在编辑器期间创建和修改好材质参数,运行时不可修改,提升数据更新和渲染的性能;UMaterialInstanceDynamic则可以运行时创建实例和修改数据,提升材质的扩展性和可定制性,但性能较UMaterialInstanceConstant差一些。UMaterialInstance需要指定一个父类,最顶层的父类要求是UMaterial实例。 **FMaterialRenderProxy是UMaterialInterface的渲染线程的代表**,类似于UPrimitiveComponent和FPrimitiveSceneProxy的关系。**FMaterialRenderProxy将UMaterialInterface实例的数据搬运(拷贝)到渲染线程,但同时也会在游戏线程被访问到,是两个线程的耦合类型,需要谨慎处理它们的数据和接口调用**。**FMaterialRenderProxy的子类对应着UMaterialInterface的子类,以便将UMaterialInterface的子类数据被精准地搬运(拷贝)到渲染线程,避免游戏线程和渲染线程的竞争**。FMaterialRenderProxy及其子类都是引擎模块的类型。 既然已经有了FMaterialRenderProxy的渲染线程代表,为什么还要存在FMaterial和FMaterialResource呢?答案有两点: - FMaterialRenderProxy及其子类是引擎模块的类型,是游戏线程和渲染线程的胶囊类,需要谨慎处理两个线程的数据和接口调用,渲染模块无法真正完全拥有它的管辖权。 - FMaterialRenderProxy的数据由UMaterialInterface传递而来,意味着FMaterialRenderProxy的信息有限,无法包含使用了材质的网格的其它信息,如顶点工厂、ShaderMap、ShaderPipelineline、FShader及各种着色器参数等。 所以,FMaterial应运而生。FMaterial同是引擎模块的类型,但存储了游戏线程和渲染线程的两个ShaderMap,意味着渲染模块可以自由地访问渲染线程的ShaderMap,而又不影响游戏线程的访问。而且FMaterial包含了渲染材质所需的所有数据,渲染器的其它地方,只要拿到网格的FMaterial,便可以正常地获取材质数据,从而提交绘制指令。比如FBasePassMeshProcessor::AddMeshBatch的代码: ```c++ // Engine\Source\Runtime\Renderer\Private\BasePassRendering.cpp void FBasePassMeshProcessor::AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId) { if (MeshBatch.bUseForMaterial) { const FMaterialRenderProxy* FallbackMaterialRenderProxyPtr = nullptr; // 获取FMaterial实例. const FMaterial& Material = MeshBatch.MaterialRenderProxy->GetMaterialWithFallback(FeatureLevel, FallbackMaterialRenderProxyPtr); const FMaterialRenderProxy& MaterialRenderProxy = FallbackMaterialRenderProxyPtr ? *FallbackMaterialRenderProxyPtr : *MeshBatch.MaterialRenderProxy; // 通过FMaterial接口获取材质数据. const EBlendMode BlendMode = Material.GetBlendMode(); const FMaterialShadingModelField ShadingModels = Material.GetShadingModels(); const bool bIsTranslucent = IsTranslucentBlendMode(BlendMode); const FMeshDrawingPolicyOverrideSettings OverrideSettings = ComputeMeshOverrideSettings(MeshBatch); const ERasterizerFillMode MeshFillMode = ComputeMeshFillMode(MeshBatch, Material, OverrideSettings); const ERasterizerCullMode MeshCullMode = ComputeMeshCullMode(MeshBatch, Material, OverrideSettings); (......) } ``` >其实应该说**FMaterial**与**FMaterialResource**从FMaterialRenderProxy中将**有多线程竞争风险的渲染所需数据进行剥离**。渲染器只需要拿到FMaterial就可以获取Shader数据并进行MeshBatch提交。 >在FMaterialRenderProxy中通过**GetMaterialXXX()** 系列函数获取FMaterial。如果Shader没有编译好会进行等待,并在等待完之后返回。GetMaterialFallbackXXX()系列函数会不断递归获取有效的FMaterial直到默认UMaterial::GetDefaultMaterial()。 >从UMaterial调用GetRenderProxy()获取**FDefaultMaterialInstance DefaultMaterialInstance**,而这个变量在**UMaterial::PostInitProperties()** 通过**DefaultMaterialInstance = new FDefaultMaterialInstance(this);** 初始化。 MaterialTemplate.ush: - **FHLSLMaterialTranslator::Translate()** 转译材质蓝图的材质节点表达式,将所有材质属性的编译结果填充到格子的FShaderCodeChunk中。 - **FHLSLMaterialTranslator::GetMaterialEnvironment()** 处理材质蓝图的编译环境(宏定义)。 - **FHLSLMaterialTranslator::GetMaterialShaderCode()**:填充MaterialTemplate.ush的空缺代码,根据Translate编译的FShaderCodeChunk对应的材质属性接口,以及其它的宏定义、结构体、工具类接口。 # UMaterial UMaterial是属于引擎层的概念,对应着我们在材质编辑器编辑的uasset资源文件,可以被应用到网格上,以便控制它在场景中的视觉效果。它继承自UMaterialInterface。 ```c++ // Engine\Source\Runtime\Engine\Classes\Materials\MaterialInterface.h // 材质的基础接口类, 定义了大量材质相关的数据和接口, 部分接口是空实现或未实现的接口. class UMaterialInterface : public UObject, public IBlendableInterface, public IInterface_AssetUserData { // 次表面散射轮廓(配置) class USubsurfaceProfile* SubsurfaceProfile; // 当图元不再被用作父元素时进行跟踪的栅栏. FRenderCommandFence ParentRefFence; protected: // Lightmass离线的GI设置. struct FLightmassMaterialInterfaceSettings LightmassSettings; // 纹理流数据. TArray TextureStreamingData; // 存于此材质资源内的用户数据列表. TArray AssetUserData; private: // 强制编译的目标Feature Level. uint32 FeatureLevelsToForceCompile; public: //---- IInterface_AssetUserData接口 ---- virtual void AddAssetUserData(UAssetUserData* InUserData) override; virtual void RemoveUserDataOfClass(TSubclassOf InUserDataClass) override; virtual UAssetUserData* GetAssetUserDataOfClass(TSubclassOf InUserDataClass) override; //---- IInterface_AssetUserData接口 ---- // 为所有材质编译的Featurelevel位域. static uint32 FeatureLevelsForAllMaterials; void SetFeatureLevelToCompile(ERHIFeatureLevel::Type FeatureLevel, bool bShouldCompile); static void SetGlobalRequiredFeatureLevel(ERHIFeatureLevel::Type FeatureLevel, bool bShouldCompile); //---- UObject接口 ---- virtual void BeginDestroy() override; virtual bool IsReadyForFinishDestroy() override; virtual void PostLoad() override; virtual void PostDuplicate(bool bDuplicateForPIE) override; virtual void PostCDOContruct() override; //---- UObject接口 ---- //---- IBlendableInterface接口 ---- virtual void OverrideBlendableSettings(class FSceneView& View, float Weight) const override; //---- IBlendableInterface接口 ---- // 沿着父链查找这个实例所在的基础材质. UMaterial* GetBaseMaterial(); // 获取正在实例化的材质 virtual class UMaterial* GetMaterial() PURE_VIRTUAL(UMaterialInterface::GetMaterial,return NULL;); virtual const class UMaterial* GetMaterial() const PURE_VIRTUAL(UMaterialInterface::GetMaterial,return NULL;); // 获取正在实例化的材质(并发模式) virtual const class UMaterial* GetMaterial_Concurrent(TMicRecursionGuard RecursionGuard = TMicRecursionGuard()) const PURE_VIRTUAL(UMaterialInterface::GetMaterial_Concurrent,return NULL;); // 测试该材质是否依赖指定的材料. virtual bool IsDependent(UMaterialInterface* TestDependency); virtual bool IsDependent_Concurrent(UMaterialInterface* TestDependency, TMicRecursionGuard RecursionGuard = TMicRecursionGuard()); // 获取此材质对应的用于渲染的FMaterialRenderProxy实例. virtual class FMaterialRenderProxy* GetRenderProxy() const PURE_VIRTUAL(UMaterialInterface::GetRenderProxy,return NULL;); (......) // 获取用于渲染此材质的纹理列表. virtual void GetUsedTextures(TArray& OutTextures, EMaterialQualityLevel::Type QualityLevel, bool bAllQualityLevels, ERHIFeatureLevel::Type FeatureLevel, bool bAllFeatureLevels) const PURE_VIRTUAL(UMaterialInterface::GetUsedTextures,); // 获取用于渲染此材质的纹理列表和索引. virtual void GetUsedTexturesAndIndices(TArray& OutTextures, TArray< TArray >& OutIndices, EMaterialQualityLevel::Type QualityLevel, ERHIFeatureLevel::Type FeatureLevel) const; // 覆盖指定的纹理. virtual void OverrideTexture(const UTexture* InTextureToOverride, UTexture* OverrideTexture, ERHIFeatureLevel::Type InFeatureLevel) PURE_VIRTUAL(UMaterialInterface::OverrideTexture, return;); // 覆盖给定参数的默认值(短暂的) virtual void OverrideVectorParameterDefault(const FHashedMaterialParameterInfo& ParameterInfo, const FLinearColor& Value, bool bOverride, ERHIFeatureLevel::Type FeatureLevel) PURE_VIRTUAL(UMaterialInterface::OverrideTexture, return;); // 检测指定的材质标记, 如果存在就返回true. virtual bool CheckMaterialUsage(const EMaterialUsage Usage) PURE_VIRTUAL(UMaterialInterface::CheckMaterialUsage,return false;); virtual bool CheckMaterialUsage_Concurrent(const EMaterialUsage Usage) const PURE_VIRTUAL(UMaterialInterface::CheckMaterialUsage,return false;); // 获取材质的渲染纹理, 需要指定FeatureLevel/QualityLevel. virtual FMaterialResource* GetMaterialResource(ERHIFeatureLevel::Type InFeatureLevel, EMaterialQualityLevel::Type QualityLevel = EMaterialQualityLevel::Num); // 获取组排序优先级. virtual bool GetGroupSortPriority(const FString& InGroupName, int32& OutSortPriority) const PURE_VIRTUAL(UMaterialInterface::GetGroupSortPriority, return false;); // 获取材质的各种类型的所有数据. virtual void GetAllScalarParameterInfo(TArray& OutParameterInfo, TArray& OutParameterIds) const PURE_VIRTUAL(UMaterialInterface::GetAllScalarParameterInfo,return;); virtual void GetAllVectorParameterInfo(TArray& OutParameterInfo, TArray& OutParameterIds) const PURE_VIRTUAL(UMaterialInterface::GetAllVectorParameterInfo,return;); virtual void GetAllTextureParameterInfo(TArray& OutParameterInfo, TArray& OutParameterIds) const PURE_VIRTUAL(UMaterialInterface::GetAllTextureParameterInfo,return;); virtual void GetAllRuntimeVirtualTextureParameterInfo(TArray& OutParameterInfo, TArray& OutParameterIds) const PURE_VIRTUAL(UMaterialInterface::GetAllRuntimeVirtualTextureParameterInfo, return;); virtual void GetAllFontParameterInfo(TArray& OutParameterInfo, TArray& OutParameterIds) const PURE_VIRTUAL(UMaterialInterface::GetAllFontParameterInfo,return;); // 获取材质的各种类型的数据. virtual bool GetScalarParameterDefaultValue(const FHashedMaterialParameterInfo& ParameterInfo, float& OutValue, bool bOveriddenOnly = false, bool bCheckOwnedGlobalOverrides = false) const PURE_VIRTUAL(UMaterialInterface::GetScalarParameterDefaultValue,return false;); virtual bool GetVectorParameterDefaultValue(const FHashedMaterialParameterInfo& ParameterInfo, FLinearColor& OutValue, bool bOveriddenOnly = false, bool bCheckOwnedGlobalOverrides = false) const PURE_VIRTUAL(UMaterialInterface::GetVectorParameterDefaultValue,return false;); virtual bool GetTextureParameterDefaultValue(const FHashedMaterialParameterInfo& ParameterInfo, class UTexture*& OutValue, bool bCheckOwnedGlobalOverrides = false) const PURE_VIRTUAL(UMaterialInterface::GetTextureParameterDefaultValue,return false;); virtual bool GetRuntimeVirtualTextureParameterDefaultValue(const FHashedMaterialParameterInfo& ParameterInfo, class URuntimeVirtualTexture*& OutValue, bool bCheckOwnedGlobalOverrides = false) const PURE_VIRTUAL(UMaterialInterface::GetRuntimeVirtualTextureParameterDefaultValue, return false;); virtual bool GetFontParameterDefaultValue(const FHashedMaterialParameterInfo& ParameterInfo, class UFont*& OutFontValue, int32& OutFontPage, bool bCheckOwnedGlobalOverrides = false) const PURE_VIRTUAL(UMaterialInterface::GetFontParameterDefaultValue,return false;); // 获取分层数据. virtual int32 GetLayerParameterIndex(EMaterialParameterAssociation Association, UMaterialFunctionInterface * LayerFunction) const PURE_VIRTUAL(UMaterialInterface::GetLayerParameterIndex, return INDEX_NONE;); // 获取由表达式引用的纹理,包括嵌套函数. virtual TArrayView GetReferencedTextures() const PURE_VIRTUAL(UMaterialInterface::GetReferencedTextures,return TArrayView();); // 保存shader稳定的键值. virtual void SaveShaderStableKeysInner(const class ITargetPlatform* TP, const struct FStableShaderKeyAndValue& SaveKeyVal) PURE_VIRTUAL(UMaterialInterface::SaveShaderStableKeysInner, ); // 获取材质参数信息. FMaterialParameterInfo GetParameterInfo(EMaterialParameterAssociation Association, FName ParameterName, UMaterialFunctionInterface* LayerFunction) const; // 获取材质关联标记. ENGINE_API FMaterialRelevance GetRelevance(ERHIFeatureLevel::Type InFeatureLevel) const; ENGINE_API FMaterialRelevance GetRelevance_Concurrent(ERHIFeatureLevel::Type InFeatureLevel) const; #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) // 记录材质和纹理. ENGINE_API virtual void LogMaterialsAndTextures(FOutputDevice& Ar, int32 Indent) const {} #endif private: int32 GetWidth() const; int32 GetHeight() const; // Lightmass接口. const FGuid& GetLightingGuid() const; void SetLightingGuid(); virtual void GetLightingGuidChain(bool bIncludeTextures, TArray& OutGuids) const; virtual bool UpdateLightmassTextureTracking(); inline bool GetOverrideCastShadowAsMasked() const; inline bool GetOverrideEmissiveBoost() const; (......) // 数据获取接口. virtual bool GetScalarParameterValue(const FHashedMaterialParameterInfo& ParameterInfo, float& OutValue, bool bOveriddenOnly = false) const; virtual bool GetScalarCurveParameterValue(const FHashedMaterialParameterInfo& ParameterInfo, FInterpCurveFloat& OutValue) const; virtual bool GetVectorParameterValue(const FHashedMaterialParameterInfo& ParameterInfo, FLinearColor& OutValue, bool bOveriddenOnly = false) const; virtual bool GetVectorCurveParameterValue(const FHashedMaterialParameterInfo& ParameterInfo, FInterpCurveVector& OutValue) const; virtual bool GetLinearColorParameterValue(const FHashedMaterialParameterInfo& ParameterInfo, FLinearColor& OutValue) const; virtual bool GetLinearColorCurveParameterValue(const FHashedMaterialParameterInfo& ParameterInfo, FInterpCurveLinearColor& OutValue) const; virtual bool GetTextureParameterValue(const FHashedMaterialParameterInfo& ParameterInfo, class UTexture*& OutValue, bool bOveriddenOnly = false) const; virtual bool GetRuntimeVirtualTextureParameterValue(const FHashedMaterialParameterInfo& ParameterInfo, class URuntimeVirtualTexture*& OutValue, bool bOveriddenOnly = false) const; virtual bool GetFontParameterValue(const FHashedMaterialParameterInfo& ParameterInfo,class UFont*& OutFontValue, int32& OutFontPage, bool bOveriddenOnly = false) const; virtual bool GetRefractionSettings(float& OutBiasValue) const; // 访问基础材质的覆盖属性. virtual float GetOpacityMaskClipValue() const; virtual bool GetCastDynamicShadowAsMasked() const; virtual EBlendMode GetBlendMode() const; virtual FMaterialShadingModelField GetShadingModels() const; virtual bool IsShadingModelFromMaterialExpression() const; virtual bool IsTwoSided() const; virtual bool IsDitheredLODTransition() const; virtual bool IsTranslucencyWritingCustomDepth() const; virtual bool IsTranslucencyWritingVelocity() const; virtual bool IsMasked() const; virtual bool IsDeferredDecal() const; virtual USubsurfaceProfile* GetSubsurfaceProfile_Internal() const; virtual bool CastsRayTracedShadows() const; // 强制流系统忽略指定持续时间的正常逻辑,而总是加载此材质使用的所有纹理的所有mip级别. virtual void SetForceMipLevelsToBeResident( bool OverrideForceMiplevelsToBeResident, bool bForceMiplevelsToBeResidentValue, float ForceDuration, int32 CinematicTextureGroups = 0, bool bFastResponse = false ); // 重新缓存所有材材质接口的统一表达式. static void RecacheAllMaterialUniformExpressions(bool bRecreateUniformBuffer); virtual void RecacheUniformExpressions(bool bRecreateUniformBuffer) const; // 初始化所有的系统默认材质. static void InitDefaultMaterials(); virtual bool IsPropertyActive(EMaterialProperty InProperty) const; static uint32 GetFeatureLevelsToCompileForAllMaterials() // 返回使用纹理坐标的数量,以及顶点数据是否在着色器图中使用. void AnalyzeMaterialProperty(EMaterialProperty InProperty, int32& OutNumTextureCoordinates, bool& bOutRequiresVertexData); // 遍历所有的FeatureLevel, 可以指定回调. template static void IterateOverActiveFeatureLevels(FunctionType InHandler); // 访问材质采样器类型的缓存的UEnum类型信息. static UEnum* GetSamplerTypeEnum(); bool UseAnyStreamingTexture() const; bool HasTextureStreamingData() const; const TArray& GetTextureStreamingData() const; FORCEINLINE TArray& GetTextureStreamingData(); // 纹理流接口. bool FindTextureStreamingDataIndexRange(FName TextureName, int32& LowerIndex, int32& HigherIndex) const; void SetTextureStreamingData(const TArray& InTextureStreamingData); // 返回纹理的比例(LocalSpace Unit / Texture), 用于纹理流矩阵. virtual float GetTextureDensity(FName TextureName, const struct FMeshUVChannelInfo& UVChannelData) const; // 预保存. virtual void PreSave(const class ITargetPlatform* TargetPlatform) override; // 按名称对纹理流数据进行排序,以加速搜索. 只在需要时排序. void SortTextureStreamingData(bool bForceSort, bool bFinalSort); protected: uint32 GetFeatureLevelsToCompileForRendering() const; void UpdateMaterialRenderProxy(FMaterialRenderProxy& Proxy); private: static void PostLoadDefaultMaterials(); // 材质采样器类型的缓存的UEnum类型信息. static UEnum* SamplerTypeEnum; }; // Engine\Source\Runtime\Engine\Classes\Materials\Material.h // 材质类, 对应着一个材质资源文件. class UMaterial : public UMaterialInterface { // 物理材质. class UPhysicalMaterial* PhysMaterial; class UPhysicalMaterialMask* PhysMaterialMask; class UPhysicalMaterial* PhysicalMaterialMap[EPhysicalMaterialMaskColor::MAX]; // 材质属性. FScalarMaterialInput Metallic; FScalarMaterialInput Specular; FScalarMaterialInput Anisotropy; FVectorMaterialInput Normal; FVectorMaterialInput Tangent; FColorMaterialInput EmissiveColor; #if WITH_EDITORONLY_DATA // 透射. FScalarMaterialInput Opacity; FScalarMaterialInput OpacityMask; #endif TEnumAsByte MaterialDomain; TEnumAsByte BlendMode; TEnumAsByte DecalBlendMode; TEnumAsByte MaterialDecalResponse; TEnumAsByte ShadingModel; UPROPERTY(AssetRegistrySearchable) FMaterialShadingModelField ShadingModels; public: // 材质属性. float OpacityMaskClipValue; FVectorMaterialInput WorldPositionOffset; FScalarMaterialInput Refraction; FMaterialAttributesInput MaterialAttributes; FScalarMaterialInput PixelDepthOffset; FShadingModelMaterialInput ShadingModelFromMaterialExpression; #if WITH_EDITORONLY_DATA FVectorMaterialInput WorldDisplacement; FScalarMaterialInput TessellationMultiplier; FColorMaterialInput SubsurfaceColor; FScalarMaterialInput ClearCoat; FScalarMaterialInput ClearCoatRoughness; FScalarMaterialInput AmbientOcclusion; FVector2MaterialInput CustomizedUVs[8]; #endif int32 NumCustomizedUVs; // 材质标记. uint8 bCastDynamicShadowAsMasked : 1; uint8 bEnableSeparateTranslucency : 1; uint8 bEnableResponsiveAA : 1; uint8 bScreenSpaceReflections : 1; uint8 bContactShadows : 1; uint8 TwoSided : 1; uint8 DitheredLODTransition : 1; uint8 DitherOpacityMask : 1; uint8 bAllowNegativeEmissiveColor : 1; // 透明相关. TEnumAsByte TranslucencyLightingMode; uint8 bEnableMobileSeparateTranslucency : 1; float TranslucencyDirectionalLightingIntensity; float TranslucentShadowDensityScale; float TranslucentSelfShadowDensityScale; float TranslucentSelfShadowSecondDensityScale; float TranslucentSelfShadowSecondOpacity; float TranslucentBackscatteringExponent; FLinearColor TranslucentMultipleScatteringExtinction; float TranslucentShadowStartOffset; // 使用标记. uint8 bDisableDepthTest : 1; uint8 bWriteOnlyAlpha : 1; uint8 bGenerateSphericalParticleNormals : 1; uint8 bTangentSpaceNormal : 1; uint8 bUseEmissiveForDynamicAreaLighting : 1; uint8 bBlockGI : 1; uint8 bUsedAsSpecialEngineMaterial : 1; uint8 bUsedWithSkeletalMesh : 1; uint8 bUsedWithEditorCompositing : 1; uint8 bUsedWithParticleSprites : 1; uint8 bUsedWithBeamTrails : 1; uint8 bUsedWithMeshParticles : 1; uint8 bUsedWithNiagaraSprites : 1; uint8 bUsedWithNiagaraRibbons : 1; uint8 bUsedWithNiagaraMeshParticles : 1; uint8 bUsedWithGeometryCache : 1; uint8 bUsedWithStaticLighting : 1; uint8 bUsedWithMorphTargets : 1; uint8 bUsedWithSplineMeshes : 1; uint8 bUsedWithInstancedStaticMeshes : 1; uint8 bUsedWithGeometryCollections : 1; uint8 bUsesDistortion : 1; uint8 bUsedWithClothing : 1; uint32 bUsedWithWater : 1; uint32 bUsedWithHairStrands : 1; uint32 bUsedWithLidarPointCloud : 1; uint32 bUsedWithVirtualHeightfieldMesh : 1; uint8 bAutomaticallySetUsageInEditor : 1; uint8 bFullyRough : 1; uint8 bUseFullPrecision : 1; uint8 bUseLightmapDirectionality : 1; uint8 bUseAlphaToCoverage : 1; uint32 bForwardRenderUsePreintegratedGFForSimpleIBL : 1; uint8 bUseHQForwardReflections : 1; uint8 bUsePlanarForwardReflections : 1; // 根据屏幕空间法向变化降低粗糙度. uint8 bNormalCurvatureToRoughness : 1; uint8 AllowTranslucentCustomDepthWrites : 1; uint8 Wireframe : 1; // 着色频率. TEnumAsByte ShadingRate; uint8 bCanMaskedBeAssumedOpaque : 1; uint8 bIsPreviewMaterial : 1; uint8 bIsFunctionPreviewMaterial : 1; uint8 bUseMaterialAttributes : 1; uint8 bCastRayTracedShadows : 1; uint8 bUseTranslucencyVertexFog : 1; uint8 bApplyCloudFogging : 1; uint8 bIsSky : 1; uint8 bComputeFogPerPixel : 1; uint8 bOutputTranslucentVelocity : 1; uint8 bAllowDevelopmentShaderCompile : 1; uint8 bIsMaterialEditorStatsMaterial : 1; TEnumAsByte BlendableLocation; uint8 BlendableOutputAlpha : 1; uint8 bEnableStencilTest : 1; TEnumAsByte StencilCompare; uint8 StencilRefValue = 0; TEnumAsByte RefractionMode; int32 BlendablePriority; uint8 bIsBlendable : 1; uint32 UsageFlagWarnings; float RefractionDepthBias; FGuid StateId; float MaxDisplacement; // 当渲染器需要获取参数值时,代表这个材质到渲染器的FMaterialRenderProxy衍生物. class FDefaultMaterialInstance* DefaultMaterialInstance; #if WITH_EDITORONLY_DATA // 编辑器参数. TMap > EditorParameters; // 材质图. 编辑器模型下的数据. class UMaterialGraph* MaterialGraph; #endif private: // 从地盘序列化而来的内联材质资源. 由游戏线程的PostLoad处理. TArray LoadedMaterialResources; // 用于渲染材质的资源列表. TArray MaterialResources; #if WITH_EDITOR // 正在缓存或烘焙的材质资源. TMap> CachedMaterialResourcesForCooking; #endif // 用于保证在清理之前使用此UMaterial中的各种资源完成RT的标志. FThreadSafeBool ReleasedByRT; FMaterialCachedExpressionData CachedExpressionData; public: //~ Begin UMaterialInterface Interface. virtual UMaterial* GetMaterial() override; virtual const UMaterial* GetMaterial() const override; virtual const UMaterial* GetMaterial_Concurrent(TMicRecursionGuard RecursionGuard = TMicRecursionGuard()) const override; virtual bool GetScalarParameterValue(...) const override; (......) void SetShadingModel(EMaterialShadingModel NewModel); virtual bool IsPropertyActive(EMaterialProperty InProperty) const override; //~ End UMaterialInterface Interface. //~ Begin UObject Interface virtual void PreSave(const class ITargetPlatform* TargetPlatform) override; virtual void PostInitProperties() override; virtual void Serialize(FArchive& Ar) override; virtual void PostDuplicate(bool bDuplicateForPIE) override; virtual void PostLoad() override; virtual void BeginDestroy() override; virtual bool IsReadyForFinishDestroy() override; virtual void FinishDestroy() override; virtual void GetResourceSizeEx(FResourceSizeEx& CumulativeResourceSize) override; static void AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector); virtual bool CanBeClusterRoot() const override; virtual void GetAssetRegistryTags(TArray& OutTags) const override; //~ End UObject Interface // 数据获取接口. bool IsDefaultMaterial() const; void ReleaseResources(); bool IsUsageFlagDirty(EMaterialUsage Usage); bool IsCompilingOrHadCompileError(ERHIFeatureLevel::Type InFeatureLevel); (......) private: // 新版的获取材质数据接口. bool GetScalarParameterValue_New(...) const; bool GetVectorParameterValue_New(...) const; bool GetTextureParameterValue_New(...) const; bool GetRuntimeVirtualTextureParameterValue_New(...) const; bool GetFontParameterValue_New(...) const; FString GetUsageName(const EMaterialUsage Usage) const; bool GetUsageByFlag(const EMaterialUsage Usage) const; bool SetMaterialUsage(bool &bNeedsRecompile, const EMaterialUsage Usage); (......) private: virtual void FlushResourceShaderMaps(); // 缓冲资源或数据. void CacheResourceShadersForRendering(bool bRegenerateId); void CacheResourceShadersForCooking(...); void CacheShadersForResources(...); public: // 静态工具箱或操作接口. static UMaterial* GetDefaultMaterial(EMaterialDomain Domain); static void UpdateMaterialShaders(...); static void BackupMaterialShadersToMemory(...); static void RestoreMaterialShadersFromMemory(...); static void CompileMaterialsForRemoteRecompile(...); static bool GetExpressionParameterName(const UMaterialExpression* Expression, FName& OutName); static bool CopyExpressionParameters(UMaterialExpression* Source, UMaterialExpression* Destination); static bool IsParameter(const UMaterialExpression* Expression); static bool IsDynamicParameter(const UMaterialExpression* Expression); static const TCHAR* GetMaterialShadingModelString(EMaterialShadingModel InMaterialShadingModel); static EMaterialShadingModel GetMaterialShadingModelFromString(const TCHAR* InMaterialShadingModelStr); static const TCHAR* GetBlendModeString(EBlendMode InBlendMode); static EBlendMode GetBlendModeFromString(const TCHAR* InBlendModeStr); virtual TArrayView GetReferencedTextures() const override final; (......) }; ``` # UMaterialInstance UMaterialInstance是材质实例,不能单独存在,而需要依赖UMaterialInterface类型的父类,意味着父类可以是UMaterialInterface的任意一个子类,但最上层的父类必须是UMaterial。 ```c++ class UMaterialInstance : public UMaterialInterface { // 物理材质. class UPhysicalMaterial* PhysMaterial; class UPhysicalMaterial* PhysicalMaterialMap[EPhysicalMaterialMaskColor::MAX]; // 材质父亲. class UMaterialInterface* Parent; // 当渲染器需要获取参数值时,代表这个材质实例的FMaterialRenderProxy的子类. class FMaterialInstanceResource* Resource; // 可以部分覆盖Parent的属性, 和UMaterial相比, 只是一小部分. uint8 bHasStaticPermutationResource:1; uint8 bOverrideSubsurfaceProfile:1; uint8 TwoSided : 1; uint8 DitheredLODTransition : 1; uint8 bCastDynamicShadowAsMasked : 1; uint8 bIsShadingModelFromMaterialExpression : 1; TEnumAsByte BlendMode; float OpacityMaskClipValue; FMaterialShadingModelField ShadingModels; // 覆盖Parent的各种类型的数据. TArray ScalarParameterValues; TArray VectorParameterValues; TArray TextureParameterValues; TArray RuntimeVirtualTextureParameterValues; TArray FontParameterValues; struct FMaterialInstanceBasePropertyOverrides BasePropertyOverrides; (......) private: FStaticParameterSet StaticParameters; FMaterialCachedParameters CachedLayerParameters; TArray CachedReferencedTextures; // 已加载的材质资源. TArray LoadedMaterialResources; TArray StaticPermutationMaterialResources; FThreadSafeBool ReleasedByRT; public: // Begin UMaterialInterface interface. virtual ENGINE_API UMaterial* GetMaterial() override; virtual ENGINE_API const UMaterial* GetMaterial() const override; virtual ENGINE_API const UMaterial* GetMaterial_Concurrent(TMicRecursionGuard RecursionGuard = TMicRecursionGuard()) const override; virtual ENGINE_API FMaterialResource* AllocatePermutationResource(); (......) //~ End UMaterialInterface Interface. //~ Begin UObject Interface. virtual ENGINE_API void GetResourceSizeEx(FResourceSizeEx& CumulativeResourceSize) override; virtual ENGINE_API void PostInitProperties() override; virtual ENGINE_API void Serialize(FArchive& Ar) override; virtual ENGINE_API void PostLoad() override; virtual ENGINE_API void BeginDestroy() override; virtual ENGINE_API bool IsReadyForFinishDestroy() override; virtual ENGINE_API void FinishDestroy() override; ENGINE_API static void AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector); //~ End UObject Interface. void GetAllShaderMaps(TArray& OutShaderMaps); void GetAllParametersOfType(EMaterialParameterType Type, TArray& OutParameterInfo, TArray& OutParameterIds) const; (......) protected: void CopyMaterialUniformParametersInternal(UMaterialInterface* Source); bool UpdateParameters(); ENGINE_API void SetParentInternal(class UMaterialInterface* NewParent, bool RecacheShaders); (......) // 初始化材质实例的资源. ENGINE_API void InitResources(); // 缓存资源. void CacheResourceShadersForRendering(); void CacheResourceShadersForRendering(FMaterialResourceDeferredDeletionArray& OutResourcesToFree); void CacheShadersForResources(...); void DeleteDeferredResources(FMaterialResourceDeferredDeletionArray& ResourcesToFree); ENGINE_API void CopyMaterialInstanceParameters(UMaterialInterface* Source); (......) }; ``` UMaterialInstance和UMaterial不一样,它需要依附于父亲实例,而且最顶层的父亲必然是UMaterial实例。它**只能覆盖UMaterial的一小部分参数**,通常不会被单独创建,而是以它的两个子类**UMaterialInstanceConstant**和**UMaterialInstanceDynamic**被创建。 # FMaterialRenderProxy FMaterialRenderProxy负责接收游戏线程代表的数据,然后传递给渲染器去处理和渲染。 ```c++ // Engine\Source\Runtime\Engine\Public\MaterialShared.h class FMaterialRenderProxy : public FRenderResource { public: // 缓存数据. mutable FUniformExpressionCacheContainer UniformExpressionCache; mutable FImmutableSamplerState ImmutableSamplerState; // 构造/析构函数. ENGINE_API FMaterialRenderProxy(); ENGINE_API virtual ~FMaterialRenderProxy(); // 计算表达式并存储到OutUniformExpressionCache. void ENGINE_API EvaluateUniformExpressions(FUniformExpressionCache& OutUniformExpressionCache, const FMaterialRenderContext& Context, class FRHICommandList* CommandListIfLocalMode = nullptr) const; // UniformExpression接口. void ENGINE_API CacheUniformExpressions(bool bRecreateUniformBuffer); void ENGINE_API CacheUniformExpressions_GameThread(bool bRecreateUniformBuffer); void ENGINE_API InvalidateUniformExpressionCache(bool bRecreateUniformBuffer); void ENGINE_API UpdateUniformExpressionCacheIfNeeded(ERHIFeatureLevel::Type InFeatureLevel) const; // 返回有效的FMaterial实例. const class FMaterial* GetMaterial(ERHIFeatureLevel::Type InFeatureLevel) const; // 查找用于渲染此FMaterialRenderProxy的FMaterial实例. virtual const FMaterial& GetMaterialWithFallback(ERHIFeatureLevel::Type InFeatureLevel, const FMaterialRenderProxy*& OutFallbackMaterialRenderProxy) const = 0; virtual FMaterial* GetMaterialNoFallback(ERHIFeatureLevel::Type InFeatureLevel) const { return NULL; } // 获取对应的UMaterialInterface实例. virtual UMaterialInterface* GetMaterialInterface() const { return NULL; } // 获取材质属性的值. virtual bool GetVectorValue(const FHashedMaterialParameterInfo& ParameterInfo, FLinearColor* OutValue, const FMaterialRenderContext& Context) const = 0; virtual bool GetScalarValue(const FHashedMaterialParameterInfo& ParameterInfo, float* OutValue, const FMaterialRenderContext& Context) const = 0; virtual bool GetTextureValue(const FHashedMaterialParameterInfo& ParameterInfo,const UTexture** OutValue, const FMaterialRenderContext& Context) const = 0; virtual bool GetTextureValue(const FHashedMaterialParameterInfo& ParameterInfo, const URuntimeVirtualTexture** OutValue, const FMaterialRenderContext& Context) const = 0; bool IsDeleted() const; void MarkForGarbageCollection(); bool IsMarkedForGarbageCollection() const; // FRenderResource interface. ENGINE_API virtual void InitDynamicRHI() override; ENGINE_API virtual void ReleaseDynamicRHI() override; ENGINE_API virtual void ReleaseResource() override; // 获取静态的材质渲染代表的映射表. ENGINE_API static const TSet& GetMaterialRenderProxyMap(); void SetSubsurfaceProfileRT(const USubsurfaceProfile* Ptr); const USubsurfaceProfile* GetSubsurfaceProfileRT() const; ENGINE_API static void UpdateDeferredCachedUniformExpressions(); static inline bool HasDeferredUniformExpressionCacheRequests(); int32 GetExpressionCacheSerialNumber() const { return UniformExpressionCacheSerialNumber; } private: const USubsurfaceProfile* SubsurfaceProfileRT; mutable int32 UniformExpressionCacheSerialNumber = 0; // 材质标记. mutable int8 MarkedForGarbageCollection : 1; mutable int8 DeletedFlag : 1; mutable int8 ReleaseResourceFlag : 1; mutable int8 HasVirtualTextureCallbacks : 1; // 追踪在所有场景的所有材质渲染代表. 只可在渲染线程访问. 用来传播新的着色器映射到渲染所用的材质. ENGINE_API static TSet MaterialRenderProxyMap; ENGINE_API static TSet DeferredUniformExpressionCacheRequests; }; ``` 我们将注意力放到两个重要的子类:FDefaultMaterialInstance和FMaterialInstanceResource,它们的定义如下: ```c++ // Engine\Source\Runtime\Engine\Private\Materials\Material.cpp // 用于渲染UMaterial的默认渲染代表, 默认的参数值已经存储于FMaterialUniformExpressionXxxParameter对象, 此资源值用来存储选中的颜色. class FDefaultMaterialInstance : public FMaterialRenderProxy { public: // 游戏线程销毁接口. void GameThread_Destroy() { FDefaultMaterialInstance* Resource = this; ENQUEUE_RENDER_COMMAND(FDestroyDefaultMaterialInstanceCommand)( [Resource](FRHICommandList& RHICmdList) { delete Resource; }); } // FMaterialRenderProxy interface. // 获取材质接口. virtual const FMaterial& GetMaterialWithFallback(ERHIFeatureLevel::Type InFeatureLevel, const FMaterialRenderProxy*& OutFallbackMaterialRenderProxy) const { const FMaterialResource* MaterialResource = Material->GetMaterialResource(InFeatureLevel); if (MaterialResource && MaterialResource->GetRenderingThreadShaderMap()) { return *MaterialResource; } OutFallbackMaterialRenderProxy = &GetFallbackRenderProxy(); return OutFallbackMaterialRenderProxy->GetMaterialWithFallback(InFeatureLevel, OutFallbackMaterialRenderProxy); } virtual FMaterial* GetMaterialNoFallback(ERHIFeatureLevel::Type InFeatureLevel) const { return Material->GetMaterialResource(InFeatureLevel); } // 获取对应的UMaterialInterface接口. virtual UMaterialInterface* GetMaterialInterface() const override { return Material; } // 获取向量的参数值. virtual bool GetVectorValue(const FHashedMaterialParameterInfo& ParameterInfo, FLinearColor* OutValue, const FMaterialRenderContext& Context) const { const FMaterialResource* MaterialResource = Material->GetMaterialResource(Context.Material.GetFeatureLevel()); if(MaterialResource && MaterialResource->GetRenderingThreadShaderMap()) { return false; } else { return GetFallbackRenderProxy().GetVectorValue(ParameterInfo, OutValue, Context); } } // 获取标量的参数值. virtual bool GetScalarValue(const FHashedMaterialParameterInfo& ParameterInfo, float* OutValue, const FMaterialRenderContext& Context) const { const FMaterialResource* MaterialResource = Material->GetMaterialResource(Context.Material.GetFeatureLevel()); if(MaterialResource && MaterialResource->GetRenderingThreadShaderMap()) { static FName NameSubsurfaceProfile(TEXT("__SubsurfaceProfile")); if (ParameterInfo.Name == NameSubsurfaceProfile) { const USubsurfaceProfile* MySubsurfaceProfileRT = GetSubsurfaceProfileRT(); int32 AllocationId = 0; if(MySubsurfaceProfileRT) { // can be optimized (cached) AllocationId = GSubsurfaceProfileTextureObject.FindAllocationId(MySubsurfaceProfileRT); } else { // no profile specified means we use the default one stored at [0] which is human skin AllocationId = 0; } *OutValue = AllocationId / 255.0f; return true; } return false; } else { return GetFallbackRenderProxy().GetScalarValue(ParameterInfo, OutValue, Context); } } // 获取纹理的参数值. virtual bool GetTextureValue(const FHashedMaterialParameterInfo& ParameterInfo,const UTexture** OutValue, const FMaterialRenderContext& Context) const { const FMaterialResource* MaterialResource = Material->GetMaterialResource(Context.Material.GetFeatureLevel()); if(MaterialResource && MaterialResource->GetRenderingThreadShaderMap()) { return false; } else { return GetFallbackRenderProxy().GetTextureValue(ParameterInfo,OutValue,Context); } } virtual bool GetTextureValue(const FHashedMaterialParameterInfo& ParameterInfo, const URuntimeVirtualTexture** OutValue, const FMaterialRenderContext& Context) const { const FMaterialResource* MaterialResource = Material->GetMaterialResource(Context.Material.GetFeatureLevel()); if (MaterialResource && MaterialResource->GetRenderingThreadShaderMap()) { return false; } else { return GetFallbackRenderProxy().GetTextureValue(ParameterInfo, OutValue, Context); } } // FRenderResource interface. virtual FString GetFriendlyName() const { return Material->GetName(); } // Constructor. FDefaultMaterialInstance(UMaterial* InMaterial); private: // 获取备份的材质渲染代理. FMaterialRenderProxy& GetFallbackRenderProxy() const { return *(UMaterial::GetDefaultMaterial(Material->MaterialDomain)->GetRenderProxy()); } // 对应的材质实例. UMaterial* Material; }; // Engine\Source\Runtime\Engine\Private\Materials\MaterialInstanceSupport.h // 渲染UMaterialInstance的材质资源. class FMaterialInstanceResource: public FMaterialRenderProxy { public: // 存储材质实例的名称和值的配对. template struct TNamedParameter { FHashedMaterialParameterInfo Info; ValueType Value; }; FMaterialInstanceResource(UMaterialInstance* InOwner); void GameThread_Destroy() { FMaterialInstanceResource* Resource = this; ENQUEUE_RENDER_COMMAND(FDestroyMaterialInstanceResourceCommand)( [Resource](FRHICommandList& RHICmdList) { delete Resource; }); } // FRenderResource interface. virtual FString GetFriendlyName() const override { return Owner->GetName(); } // FMaterialRenderProxy interface. // 获取材质渲染资源. virtual const FMaterial& GetMaterialWithFallback(ERHIFeatureLevel::Type FeatureLevel, const FMaterialRenderProxy*& OutFallbackMaterialRenderProxy) const override; virtual FMaterial* GetMaterialNoFallback(ERHIFeatureLevel::Type FeatureLevel) const override; virtual UMaterialInterface* GetMaterialInterface() const override; // 获取材质的值. virtual bool GetVectorValue(const FHashedMaterialParameterInfo& ParameterInfo, FLinearColor* OutValue, const FMaterialRenderContext& Context) const override; virtual bool GetScalarValue(const FHashedMaterialParameterInfo& ParameterInfo, float* OutValue, const FMaterialRenderContext& Context) const override; virtual bool GetTextureValue(const FHashedMaterialParameterInfo& ParameterInfo, const UTexture** OutValue, const FMaterialRenderContext& Context) const override; virtual bool GetTextureValue(const FHashedMaterialParameterInfo& ParameterInfo, const URuntimeVirtualTexture** OutValue, const FMaterialRenderContext& Context) const override; void GameThread_SetParent(UMaterialInterface* ParentMaterialInterface); void InitMIParameters(struct FMaterialInstanceParameterSet& ParameterSet); void RenderThread_ClearParameters() { VectorParameterArray.Empty(); ScalarParameterArray.Empty(); TextureParameterArray.Empty(); RuntimeVirtualTextureParameterArray.Empty(); InvalidateUniformExpressionCache(false); } // 更新参数. template void RenderThread_UpdateParameter(const FHashedMaterialParameterInfo& ParameterInfo, const ValueType& Value ) { LLM_SCOPE(ELLMTag::MaterialInstance); InvalidateUniformExpressionCache(false); TArray >& ValueArray = GetValueArray(); const int32 ParameterCount = ValueArray.Num(); for (int32 ParameterIndex = 0; ParameterIndex < ParameterCount; ++ParameterIndex) { TNamedParameter& Parameter = ValueArray[ParameterIndex]; if (Parameter.Info == ParameterInfo) { Parameter.Value = Value; return; } } TNamedParameter NewParameter; NewParameter.Info = ParameterInfo; NewParameter.Value = Value; ValueArray.Add(NewParameter); } // 查找指定名字的参数值. template const ValueType* RenderThread_FindParameterByName(const FHashedMaterialParameterInfo& ParameterInfo) const { const TArray >& ValueArray = GetValueArray(); const int32 ParameterCount = ValueArray.Num(); for (int32 ParameterIndex = 0; ParameterIndex < ParameterCount; ++ParameterIndex) { const TNamedParameter& Parameter = ValueArray[ParameterIndex]; if (Parameter.Info == ParameterInfo) { return &Parameter.Value; } } return NULL; } private: template TArray >& GetValueArray(); // 材质实例的父亲. UMaterialInterface* Parent; // 游戏线程的父亲. UMaterialInterface* GameThreadParent; // 所属的材质实例. UMaterialInstance* Owner; // 各种类型的参数值列表. TArray > VectorParameterArray; TArray > ScalarParameterArray; TArray > TextureParameterArray; TArray > RuntimeVirtualTextureParameterArray; }; ``` 需要格外注意的是,FMaterialRenderProxy既会被游戏线程处理,又会被渲染线程处理,需要小心注意它们之间的数据访问和接口调用。带有GameThread的是专用于游戏线程,带有RenderThread的专用于渲染线程,如果没有特别说明,一般(非绝对)用于渲染线程。 # FMaterial & FMaterialResource FMaterial有3个功能: - 表示材质到材质的编译过程,并提供可扩展性钩子(CompileProperty等) 。 - 将材质数据传递到渲染器,并使用函数访问材质属性。 - 存储缓存的shader map和其他来自编译的瞬态输出,这对异步着色器编译是必要的。 下面是FMaterial的定义: ```c++ // Engine\Source\Runtime\Engine\Public\MaterialShared.h class FMaterial { public: #if UE_CHECK_FMATERIAL_LIFETIME uint32 AddRef() const; uint32 Release() const; inline uint32 GetRefCount() const { return uint32(NumDebugRefs.GetValue()); } mutable FThreadSafeCounter NumDebugRefs; #else FORCEINLINE uint32 AddRef() const { return 0u; } FORCEINLINE uint32 Release() const { return 0u; } FORCEINLINE uint32 GetRefCount() const { return 0u; } #endif FMaterial(); ENGINE_API virtual ~FMaterial(); // 缓存shader. ENGINE_API bool CacheShaders(EShaderPlatform Platform, const ITargetPlatform* TargetPlatform = nullptr); ENGINE_API bool CacheShaders(const FMaterialShaderMapId& ShaderMapId, EShaderPlatform Platform, const ITargetPlatform* TargetPlatform = nullptr); // 是否需要缓存指定shader type的数据. ENGINE_API virtual bool ShouldCache(EShaderPlatform Platform, const FShaderType* ShaderType, const FVertexFactoryType* VertexFactoryType) const; ENGINE_API bool ShouldCachePipeline(EShaderPlatform Platform, const FShaderPipelineType* PipelineType, const FVertexFactoryType* VertexFactoryType) const; // 序列化. ENGINE_API virtual void LegacySerialize(FArchive& Ar); void SerializeInlineShaderMap(FArchive& Ar); // ShaderMap接口. void RegisterInlineShaderMap(bool bLoadedByCookedMaterial); void ReleaseShaderMap(); void DiscardShaderMap(); // 材质属性. ENGINE_API virtual void GetShaderMapId(EShaderPlatform Platform, const ITargetPlatform* TargetPlatform, FMaterialShaderMapId& OutId) const; virtual EMaterialDomain GetMaterialDomain() const = 0; // See EMaterialDomain. virtual bool IsTwoSided() const = 0; virtual bool IsDitheredLODTransition() const = 0; virtual bool IsTranslucencyWritingCustomDepth() const { return false; } virtual bool IsTranslucencyWritingVelocity() const { return false; } virtual bool IsTangentSpaceNormal() const { return false; } (......) // 是否需要保存到磁盘. virtual bool IsPersistent() const = 0; // 获取材质实例. virtual UMaterialInterface* GetMaterialInterface() const { return NULL; } ENGINE_API bool HasValidGameThreadShaderMap() const; inline bool ShouldCastDynamicShadows() const; EMaterialQualityLevel::Type GetQualityLevel() const // 数据访问接口. ENGINE_API const FUniformExpressionSet& GetUniformExpressions() const; ENGINE_API TArrayView GetUniformTextureExpressions(EMaterialTextureParameterType Type) const; ENGINE_API TArrayView GetUniformVectorParameterExpressions() const; ENGINE_API TArrayView GetUniformScalarParameterExpressions() const; inline TArrayView GetUniform2DTextureExpressions() const { return GetUniformTextureExpressions(EMaterialTextureParameterType::Standard2D); } inline TArrayView GetUniformCubeTextureExpressions() const { return GetUniformTextureExpressions(EMaterialTextureParameterType::Cube); } inline TArrayView GetUniform2DArrayTextureExpressions() const { return GetUniformTextureExpressions(EMaterialTextureParameterType::Array2D); } inline TArrayView GetUniformVolumeTextureExpressions() const { return GetUniformTextureExpressions(EMaterialTextureParameterType::Volume); } inline TArrayView GetUniformVirtualTextureExpressions() const { return GetUniformTextureExpressions(EMaterialTextureParameterType::Virtual); } const FStaticFeatureLevel GetFeatureLevel() const { return FeatureLevel; } bool GetUsesDynamicParameter() const; ENGINE_API bool RequiresSceneColorCopy_GameThread() const; ENGINE_API bool RequiresSceneColorCopy_RenderThread() const; ENGINE_API bool NeedsSceneTextures() const; ENGINE_API bool NeedsGBuffer() const; ENGINE_API bool UsesEyeAdaptation() const; ENGINE_API bool UsesGlobalDistanceField_GameThread() const; ENGINE_API bool UsesWorldPositionOffset_GameThread() const; // 材质标记. ENGINE_API bool MaterialModifiesMeshPosition_RenderThread() const; ENGINE_API bool MaterialModifiesMeshPosition_GameThread() const; ENGINE_API bool MaterialUsesPixelDepthOffset() const; ENGINE_API bool MaterialUsesDistanceCullFade_GameThread() const; ENGINE_API bool MaterialUsesSceneDepthLookup_RenderThread() const; ENGINE_API bool MaterialUsesSceneDepthLookup_GameThread() const; ENGINE_API bool UsesCustomDepthStencil_GameThread() const; ENGINE_API bool MaterialMayModifyMeshPosition() const; ENGINE_API bool MaterialUsesAnisotropy_GameThread() const; ENGINE_API bool MaterialUsesAnisotropy_RenderThread() const; // shader map接口. class FMaterialShaderMap* GetGameThreadShaderMap() const { return GameThreadShaderMap; } void SetGameThreadShaderMap(FMaterialShaderMap* InMaterialShaderMap) { GameThreadShaderMap = InMaterialShaderMap; TRefCountPtr ShaderMap = GameThreadShaderMap; TRefCountPtr Material = this; // 将游戏线程的shader map设置到渲染线程. ENQUEUE_RENDER_COMMAND(SetGameThreadShaderMap)([Material = MoveTemp(Material), ShaderMap = MoveTemp(ShaderMap)](FRHICommandListImmediate& RHICmdList) mutable { Material->RenderingThreadShaderMap = MoveTemp(ShaderMap); }); } void SetInlineShaderMap(FMaterialShaderMap* InMaterialShaderMap); ENGINE_API class FMaterialShaderMap* GetRenderingThreadShaderMap() const; ENGINE_API void SetRenderingThreadShaderMap(const TRefCountPtr& InMaterialShaderMap); ENGINE_API virtual void AddReferencedObjects(FReferenceCollector& Collector); virtual TArrayView GetReferencedTextures() const = 0; // 获取shader/shader pipeline. template TShaderRef GetShader(FVertexFactoryType* VertexFactoryType, const typename ShaderType::FPermutationDomain& PermutationVector, bool bFatalIfMissing = true) const; template TShaderRef GetShader(FVertexFactoryType* VertexFactoryType, int32 PermutationId = 0, bool bFatalIfMissing = true) const; ENGINE_API FShaderPipelineRef GetShaderPipeline(class FShaderPipelineType* ShaderPipelineType, FVertexFactoryType* VertexFactoryType, bool bFatalIfNotFound = true) const; // 材质接口. virtual FString GetMaterialUsageDescription() const = 0; virtual bool GetAllowDevelopmentShaderCompile()const{ return true; } virtual EMaterialShaderMapUsage::Type GetMaterialShaderMapUsage() const { return EMaterialShaderMapUsage::Default; } ENGINE_API bool GetMaterialExpressionSource(FString& OutSource); ENGINE_API bool WritesEveryPixel(bool bShadowPass = false) const; virtual void SetupExtaCompilationSettings(const EShaderPlatform Platform, FExtraShaderCompilerSettings& Settings) const; (......) protected: const FMaterialShaderMap* GetShaderMapToUse() const; virtual int32 CompilePropertyAndSetMaterialProperty(EMaterialProperty Property, class FMaterialCompiler* Compiler, EShaderFrequency OverrideShaderFrequency = SF_NumFrequencies, bool bUsePreviousFrameTime = false) const = 0; void SetQualityLevelProperties(ERHIFeatureLevel::Type InFeatureLevel, EMaterialQualityLevel::Type InQualityLevel = EMaterialQualityLevel::Num); virtual EMaterialShaderMapUsage::Type GetShaderMapUsage() const; virtual FGuid GetMaterialId() const = 0; ENGINE_API void GetDependentShaderAndVFTypes(EShaderPlatform Platform, TArray& OutShaderTypes, TArray& OutShaderPipelineTypes, TArray& OutVFTypes) const; bool GetLoadedCookedShaderMapId() const; private: // 游戏线程和渲染线程的shader map. TRefCountPtr GameThreadShaderMap; TRefCountPtr RenderingThreadShaderMap; // 质量等级. EMaterialQualityLevel::Type QualityLevel; ERHIFeatureLevel::Type FeatureLevel; // 特殊标记. uint32 bStencilDitheredLOD : 1; uint32 bContainsInlineShaders : 1; uint32 bLoadedCookedShaderMapId : 1; bool BeginCompileShaderMap( const FMaterialShaderMapId& ShaderMapId, const FStaticParameterSet &StaticParameterSet, EShaderPlatform Platform, TRefCountPtr& OutShaderMap, const ITargetPlatform* TargetPlatform = nullptr); void SetupMaterialEnvironment( EShaderPlatform Platform, const FShaderParametersMetadata& InUniformBufferStruct, const FUniformExpressionSet& InUniformExpressionSet, FShaderCompilerEnvironment& OutEnvironment ) const; ENGINE_API TShaderRef GetShader(class FMeshMaterialShaderType* ShaderType, FVertexFactoryType* VertexFactoryType, int32 PermutationId, bool bFatalIfMissing = true) const; }; ``` 由上面可知,FMaterial集大之所成,囊括了材质、Shader、VertexFactory、ShaderPipeline、ShaderMap等各种数据和操作接口,是这些数据的集散地。不过,它只是个抽象的父类,具体的功能需要由子类实现。它的子类只有FMaterialResource: ```c++ // 实现FMaterial的接口, 用于渲染UMaterial或UMaterialInstance. class FMaterialResource : public FMaterial { public: ENGINE_API FMaterialResource(); ENGINE_API virtual ~FMaterialResource(); // 设置材质. void SetMaterial(UMaterial* InMaterial, UMaterialInstance* InInstance, ERHIFeatureLevel::Type InFeatureLevel, EMaterialQualityLevel::Type InQualityLevel = EMaterialQualityLevel::Num) { Material = InMaterial; MaterialInstance = InInstance; SetQualityLevelProperties(InFeatureLevel, InQualityLevel); } ENGINE_API uint32 GetNumVirtualTextureStacks() const; ENGINE_API virtual FString GetMaterialUsageDescription() const override; // FMaterial interface. ENGINE_API virtual void GetShaderMapId(EShaderPlatform Platform, const ITargetPlatform* TargetPlatform, FMaterialShaderMapId& OutId) const override; ENGINE_API virtual EMaterialDomain GetMaterialDomain() const override; ENGINE_API virtual bool IsTwoSided() const override; ENGINE_API virtual bool IsDitheredLODTransition() const override; ENGINE_API virtual bool IsTranslucencyWritingCustomDepth() const override; ENGINE_API virtual bool IsTranslucencyWritingVelocity() const override; ENGINE_API virtual bool IsTangentSpaceNormal() const override; ENGINE_API virtual EMaterialShadingRate GetShadingRate() const override; (......) // 材质接口. inline const UMaterial* GetMaterial() const { return Material; } inline const UMaterialInstance* GetMaterialInstance() const { return MaterialInstance; } inline void SetMaterial(UMaterial* InMaterial) { Material = InMaterial; } inline void SetMaterialInstance(UMaterialInstance* InMaterialInstance) { MaterialInstance = InMaterialInstance; } protected: // 对应的材质. UMaterial* Material; // 对应的材质实例. UMaterialInstance* MaterialInstance; // 编译指定材质属性的入口, 须有SetMaterialProperty调用. ENGINE_API virtual int32 CompilePropertyAndSetMaterialProperty(EMaterialProperty Property, class FMaterialCompiler* Compiler, EShaderFrequency OverrideShaderFrequency, bool bUsePreviousFrameTime) const override; ENGINE_API virtual bool HasVertexPositionOffsetConnected() const override; ENGINE_API virtual bool HasPixelDepthOffsetConnected() const override; ENGINE_API virtual bool HasMaterialAttributesConnected() const override; (......) }; ``` FMaterialResource只是实现了FMaterial未实现的接口,并且存储了UMaterial或UMaterialInstance的实例。如果UMaterialInstance和UMaterial的实例都有效的情况下,那么它们重叠的数据会优先取UMaterialInstance的数据,比如: ```c++ // 获取着色模型域 FMaterialShadingModelField FMaterialResource::GetShadingModels() const { // 优先选用MaterialInstance的数据. return MaterialInstance ? MaterialInstance->GetShadingModels() : Material->GetShadingModels(); } ``` 渲染资源除了FMaterial之外,还有个比较核心的概念就是**FMaterialRenderContext**,它保存了**FMaterialRenderProxy和FMaterial**之间的关联配对: ```c++ struct ENGINE_API FMaterialRenderContext { // 用于材质shader的材质渲染代表. const FMaterialRenderProxy* MaterialRenderProxy; // 材质渲染资源. const FMaterial& Material; // 是否显示选中时的颜色. bool bShowSelection; // 构造函数. FMaterialRenderContext(const FMaterialRenderProxy* InMaterialRenderProxy, const FMaterial& InMaterial, const FSceneView* InView); }; ``` FMaterialRenderContext较多地用于材质各种类型的接口的形参,比如: ```c++ // FDefaultMaterialInstance中的获取向量参数值, 用到了FMaterialRenderContext参数. virtual bool FDefaultMaterialInstance::GetVectorValue(const FHashedMaterialParameterInfo& ParameterInfo, FLinearColor* OutValue, const FMaterialRenderContext& Context) const { const FMaterialResource* MaterialResource = Material->GetMaterialResource(Context.Material.GetFeatureLevel()); if(MaterialResource && MaterialResource->GetRenderingThreadShaderMap()) { return false; } else { return GetFallbackRenderProxy().GetVectorValue(ParameterInfo, OutValue, Context); } } ``` # 材质渲染 材质数据的发起者依然是游戏线程侧的资源,一般是从磁盘加载的二进制资源,然后序列化成UMaterialInterface实例,或者由运行时动态创建并设置材质数据。不过绝大多数是由磁盘加载而来。 当使用了材质的图元组件在被要求**收集MeshBatch**的时候,可以将其使用的UMaterialInterface对应的**FMaterialRenderProxy传递到FMeshBatchElement**中。下面以StaticMesh为例: ```c++ // Engine\Source\Runtime\Engine\Private\StaticMeshRender.cpp bool FStaticMeshSceneProxy::GetMeshElement( int32 LODIndex, int32 BatchIndex, int32 SectionIndex, uint8 InDepthPriorityGroup, bool bUseSelectionOutline, bool bAllowPreCulledIndices, FMeshBatch& OutMeshBatch) const { const ERHIFeatureLevel::Type FeatureLevel = GetScene().GetFeatureLevel(); const FStaticMeshLODResources& LOD = RenderData->LODResources[LODIndex]; const FStaticMeshVertexFactories& VFs = RenderData->LODVertexFactories[LODIndex]; const FStaticMeshSection& Section = LOD.Sections[SectionIndex]; const FLODInfo& ProxyLODInfo = LODs[LODIndex]; // 获取材质的各种实例(包含UMaterialInterface, FMaterialRenderProxy和FMaterial) UMaterialInterface* MaterialInterface = ProxyLODInfo.Sections[SectionIndex].Material; FMaterialRenderProxy* MaterialRenderProxy = MaterialInterface->GetRenderProxy(); const FMaterial* Material = MaterialRenderProxy->GetMaterial(FeatureLevel); FMeshBatchElement& OutMeshBatchElement = OutMeshBatch.Elements[0]; // 处理顶点工厂 const FVertexFactory* VertexFactory = nullptr; if (ProxyLODInfo.OverrideColorVertexBuffer) { (......) } (......) if(NumPrimitives > 0) { OutMeshBatch.SegmentIndex = SectionIndex; OutMeshBatch.LODIndex = LODIndex; // 赋值材质和渲染代表. OutMeshBatch.MaterialRenderProxy = MaterialRenderProxy; (......) } } ``` 因此,可以知道,在组件收集网格元素的时候,材质的所有类型的数据已经准备好,并且可以被访问了。说明在游戏线程阶段,材质的各种类型的实例已经被加载、设置和创建。我们继续深究到底是什么时候创建的。首先看FMaterialRenderProxy,不同的UMaterialInterface的子类稍有不一样,具体如下代码所示: ```c++ // Engine\Source\Runtime\Engine\Private\Materials\MaterialInstance.cpp void UMaterialInstance::PostInitProperties() { Super::PostInitProperties(); if(!HasAnyFlags(RF_ClassDefaultObject)) { // 创建FMaterialRenderProxy. Resource = new FMaterialInstanceResource(this); } } FMaterialRenderProxy* UMaterialInstance::GetRenderProxy() const { return Resource; } // Engine\Source\Runtime\Engine\Private\Materials\Material.cpp void UMaterial::PostInitProperties() { Super::PostInitProperties(); if(!HasAnyFlags(RF_ClassDefaultObject)) { // 创建FMaterialRenderProxy. DefaultMaterialInstance = new FDefaultMaterialInstance(this); } FPlatformMisc::CreateGuid(StateId); } FMaterialRenderProxy* UMaterial::GetRenderProxy() const { return DefaultMaterialInstance; } ``` 由此可推断,UMaterialInstance对应的FMaterialRenderProxy是在子类的PostInitProperties阶段被创建的。 我们继续查明UMaterialInterface获取对应的FMaterial实例是哪个接口哪个成员: ```c++ // Engine\Source\Runtime\Engine\Private\Materials\Material.cpp // 获取UMaterial对应的FMaterialResource(FMaterial的子类)实例. FMaterialResource* UMaterial::GetMaterialResource(ERHIFeatureLevel::Type InFeatureLevel, EMaterialQualityLevel::Type QualityLevel) { if (QualityLevel == EMaterialQualityLevel::Num) { QualityLevel = GetCachedScalabilityCVars().MaterialQualityLevel; } return FindMaterialResource(MaterialResources, InFeatureLevel, QualityLevel, true); } ``` 以上可以知道,是查找UMaterial::MaterialResources,那么继续深究其何时被创建: ```c++ FMaterialResource* FindOrCreateMaterialResource(TArray& MaterialResources, UMaterial* OwnerMaterial, UMaterialInstance* OwnerMaterialInstance, ERHIFeatureLevel::Type InFeatureLevel, EMaterialQualityLevel::Type InQualityLevel) { (......) FMaterialResource* CurrentResource = FindMaterialResource(MaterialResources, InFeatureLevel, QualityLevelForResource, false); // 如果当前资源列表不存在就创建新的FMaterialResource实例. if (!CurrentResource) { // 优先使用材质实例的的接口来创建. CurrentResource = OwnerMaterialInstance ? OwnerMaterialInstance->AllocatePermutationResource() : OwnerMaterial->AllocateResource(); CurrentResource->SetMaterial(OwnerMaterial, OwnerMaterialInstance, InFeatureLevel, QualityLevelForResource); // 添加到FMaterialResource实例列表. MaterialResources.Add(CurrentResource); } (......) return CurrentResource; } ``` 以上创建FMaterialResource实例时会优先使用有效的OwnerMaterialInstance,然后才使用UMaterial的接口,下面进入它们创建FMaterialResource实例的接口: ```c++ FMaterialResource* UMaterialInstance::AllocatePermutationResource() { return new FMaterialResource(); } FMaterialResource* UMaterial::AllocateResource() { return new FMaterialResource(); } ``` 好家伙,逻辑一样的,都是直接new一个FMaterialResource对象并返回。下面继续追踪有哪些接口会调用FindOrCreateMaterialResource: - ProcessSerializedInlineShaderMaps - UMaterial::PostLoad - UMaterial::CacheResourceShadersForRendering - UMaterial::AllMaterialsCacheResourceShadersForRendering - UMaterial::ForceRecompileForRendering - UMaterial::PostEditChangePropertyInternal - UMaterial::SetMaterialUsage - UMaterial::UpdateMaterialShaders - UMaterial::UpdateMaterialShaderCacheAndTextureReferences 以上接口都会直接或间接调用到FindOrCreateMaterialResource接口,从而触发FMaterialResource对象的创建。但在运行时的版本中,通常由UMaterial::PostLoad触发,调用堆栈如下所示: - UMaterial::PostLoad - ProcessSerializedInlineShaderMaps - FindOrCreateMaterialResource 此外,UMaterialInstance的部分接口也会触发FMaterialResource实例的创建,此文不继续追踪了。 我们继续研究FMaterial的GameThreadShaderMap和RenderingThreadShaderMap是在何处何时被设置和传递的: ```c++ // 直接设置RenderingThreadShaderMap void FMaterial::SetRenderingThreadShaderMap(const TRefCountPtr& InMaterialShaderMap) { RenderingThreadShaderMap = InMaterialShaderMap; } // 设置游戏线程ShaderMap. void FMaterial::SetGameThreadShaderMap(FMaterialShaderMap* InMaterialShaderMap) { GameThreadShaderMap = InMaterialShaderMap; TRefCountPtr ShaderMap = GameThreadShaderMap; TRefCountPtr Material = this; // 向渲染线程推送设置ShaderMap的指令. ENQUEUE_RENDER_COMMAND(SetGameThreadShaderMap)([Material = MoveTemp(Material), ShaderMap = MoveTemp(ShaderMap)](FRHICommandListImmediate& RHICmdList) mutable { Material->RenderingThreadShaderMap = MoveTemp(ShaderMap); }); } // 设置内联ShaderMap void FMaterial::SetInlineShaderMap(FMaterialShaderMap* InMaterialShaderMap) { GameThreadShaderMap = InMaterialShaderMap; bContainsInlineShaders = true; bLoadedCookedShaderMapId = true; TRefCountPtr ShaderMap = GameThreadShaderMap; TRefCountPtr Material = this; // 向渲染线程推送设置ShaderMap的指令. ENQUEUE_RENDER_COMMAND(SetInlineShaderMap)([Material = MoveTemp(Material), ShaderMap = MoveTemp(ShaderMap)](FRHICommandListImmediate& RHICmdList) mutable { Material->RenderingThreadShaderMap = MoveTemp(ShaderMap); }); } ``` 以上可以设置FMaterial的RenderingThreadShaderMap有3个接口,继续追踪有哪些接口会调用到它们: - FMaterial::CacheShaders - FMaterial::SetGameThreadShaderMap - FMaterialShaderMap::LoadForRemoteRecompile - FMaterial::SetGameThreadShaderMap - ProcessSerializedInlineShaderMaps - FMaterial::SetInlineShaderMap - SetShaderMapsOnMaterialResources_RenderThread - FMaterial::SetRenderingThreadShaderMap 虽然上面有很多接口最终会设置到FMaterial的RenderingThreadShaderMap,不过多数情况下,运行时RenderingThreadShaderMap被设置的调用堆栈如下: - UMaterial::PostLoad - ProcessSerializedInlineShaderMaps - FMaterial::SetInlineShaderMap 一旦FMaterial的RenderingThreadShaderMap被正确设置,材质相关的其它众多数据将被渲染线程和渲染器自由地读取,如同鱼儿无忧无虑地遨游在湛蓝的大海之中。 # 材质编译 UMaterialExpression就是表达式,每个材质节点UMaterialGraphNode都有一个UMaterialExpression实例。下面进入FMaterialCompiler(是抽象类,由子类FHLSLMaterialTranslator实现)的这两个接口的实现: ```c++ // Engine\Source\Runtime\Engine\Private\Materials\HLSLMaterialTranslator.cpp int32 FHLSLMaterialTranslator::Add(int32 A,int32 B) { if(A == INDEX_NONE || B == INDEX_NONE) { return INDEX_NONE; } const uint64 Hash = CityHash128to64({ GetParameterHash(A), GetParameterHash(B) }); if(GetParameterUniformExpression(A) && GetParameterUniformExpression(B)) { return AddUniformExpressionWithHash(Hash, new FMaterialUniformExpressionFoldedMath(GetParameterUniformExpression(A),GetParameterUniformExpression(B),FMO_Add),GetArithmeticResultType(A,B),TEXT("(%s + %s)"),*GetParameterCode(A),*GetParameterCode(B)); } else { return AddCodeChunkWithHash(Hash, GetArithmeticResultType(A,B),TEXT("(%s + %s)"),*GetParameterCode(A),*GetParameterCode(B)); } } int32 FHLSLMaterialTranslator::DDX( int32 X ) { if (X == INDEX_NONE) { return INDEX_NONE; } if (ShaderFrequency == SF_Compute) { // running a material in a compute shader pass (e.g. when using SVOGI) return AddInlinedCodeChunk(MCT_Float, TEXT("0")); } if (ShaderFrequency != SF_Pixel) { return NonPixelShaderExpressionError(); } return AddCodeChunk(GetParameterType(X),TEXT("DDX(%s)"),*GetParameterCode(X)); } ``` **UMaterialGraphNode即我们在材质编辑器创建的材质节点**,继承的父类依次是UMaterialGraphNode_Base、UEdGraphNode。 ## FHLSLMaterialTranslator FHLSLMaterialTranslator继承自FMaterialCompiler,作用就是将材质的表达式转译成HLSL代码,填充到MaterialTemplate.ush的宏和空缺代码段。 ```c++ // Engine\Source\Runtime\Engine\Public\MaterialCompiler.h class FMaterialCompiler { public: virtual ~FMaterialCompiler() { } // 材质属性接口. virtual void SetMaterialProperty(EMaterialProperty InProperty, EShaderFrequency OverrideShaderFrequency = SF_NumFrequencies, bool bUsePreviousFrameTime = false) = 0; virtual void PushMaterialAttribute(const FGuid& InAttributeID) = 0; virtual FGuid PopMaterialAttribute() = 0; virtual const FGuid GetMaterialAttribute() = 0; virtual void SetBaseMaterialAttribute(const FGuid& InAttributeID) = 0; virtual void PushParameterOwner(const FMaterialParameterInfo& InOwnerInfo) = 0; virtual FMaterialParameterInfo PopParameterOwner() = 0; // 调用材质表达式. virtual int32 CallExpression(FMaterialExpressionKey ExpressionKey,FMaterialCompiler* InCompiler) = 0; // 平台和着色模型相关. virtual EShaderFrequency GetCurrentShaderFrequency() const = 0; virtual EMaterialCompilerType GetCompilerType() const; inline bool IsVertexInterpolatorBypass() const; virtual EMaterialValueType GetType(int32 Code) = 0; virtual EMaterialQualityLevel::Type GetQualityLevel() = 0; virtual ERHIFeatureLevel::Type GetFeatureLevel() = 0; virtual EShaderPlatform GetShaderPlatform() = 0; virtual const ITargetPlatform* GetTargetPlatform() const = 0; virtual FMaterialShadingModelField GetMaterialShadingModels() const = 0; (......) // 材质表达式对应的接口. virtual int32 AccessCollectionParameter(UMaterialParameterCollection* ParameterCollection, int32 ParameterIndex, int32 ComponentIndex) = 0; virtual int32 ScalarParameter(FName ParameterName, float DefaultValue) = 0; virtual int32 VectorParameter(FName ParameterName, const FLinearColor& DefaultValue) = 0; virtual int32 Constant(float X) = 0; virtual int32 Constant2(float X,float Y) = 0; virtual int32 Sine(int32 X) = 0; virtual int32 Cosine(int32 X) = 0; virtual int32 Tangent(int32 X) = 0; virtual int32 ReflectionVector() = 0; virtual int32 If(int32 A,int32 B,int32 AGreaterThanB,int32 AEqualsB,int32 ALessThanB,int32 Threshold) = 0; virtual int32 VertexInterpolator(uint32 InterpolatorIndex) = 0; virtual int32 Add(int32 A,int32 B) = 0; virtual int32 Sub(int32 A,int32 B) = 0; virtual int32 Mul(int32 A,int32 B) = 0; virtual int32 Div(int32 A,int32 B) = 0; virtual int32 Dot(int32 A,int32 B) = 0; virtual int32 Cross(int32 A,int32 B) = 0; virtual int32 DDX(int32 X) = 0; virtual int32 DDY(int32 X) = 0; (......) }; // Engine\Source\Runtime\Engine\Private\Materials\HLSLMaterialTranslator.h class FHLSLMaterialTranslator : public FMaterialCompiler { protected: // 编译的材质. FMaterial* Material; // 编译输出结果, 会被存储到DDC. FMaterialCompilationOutput& MaterialCompilationOutput; // 资源字符串. FString ResourcesString; // MaterialTemplate.usf字符串内容. FString MaterialTemplate; // 平台相关. EShaderFrequency ShaderFrequency; EShaderPlatform Platform; EMaterialQualityLevel::Type QualityLevel; ERHIFeatureLevel::Type FeatureLevel; FMaterialShadingModelField ShadingModelsFromCompilation; const ITargetPlatform* TargetPlatform; // 编译的中间数据. EMaterialProperty MaterialProperty; TArray MaterialAttributesStack; TArray ParameterOwnerStack; TArray* CurrentScopeChunks; bool SharedPixelProperties[CompiledMP_MAX]; TArray FunctionStacks[SF_NumFrequencies]; FStaticParameterSet StaticParameters; TArray SharedPropertyCodeChunks[SF_NumFrequencies]; TArray UniformExpressions; TArray > UniformVectorExpressions; TArray > UniformScalarExpressions; TArray > UniformTextureExpressions[NumMaterialTextureParameterTypes]; TArray> UniformExternalTextureExpressions; TArray ParameterCollections; TArray CustomExpressions; TArray CustomOutputImplementations; TArray CustomVertexInterpolators; // 顶点工厂栈入口. TArray VTStacks; FHashTable VTStackHash; TBitArray<> AllocatedUserTexCoords; TBitArray<> AllocatedUserVertexTexCoords; (.....) public: // 执行HLSL转译. bool Translate(); // 获取材质环境. void GetMaterialEnvironment(EShaderPlatform InPlatform, FShaderCompilerEnvironment& OutEnvironment); void GetSharedInputsMaterialCode(FString& PixelMembersDeclaration, FString& NormalAssignment, FString& PixelMembersInitializationEpilog); // 获取材质着色器代码. FString GetMaterialShaderCode(); protected: // 获取所有定义. FString GetDefinitions(TArray& CodeChunks, int32 StartChunk, int32 EndChunk) const; // 代码块. int32 AddCodeChunkInner(uint64 Hash, const TCHAR* FormattedCode, EMaterialValueType Type, bool bInlined); int32 AddCodeChunk(EMaterialValueType Type, const TCHAR* Format, ...); int32 AddCodeChunkWithHash(uint64 BaseHash, EMaterialValueType Type, const TCHAR* Format, ...); int32 AddInlinedCodeChunk(EMaterialValueType Type, const TCHAR* Format, ...); int32 AddInlinedCodeChunkWithHash(uint64 BaseHash, EMaterialValueType Type, const TCHAR* Format, ...); int32 AddUniformExpressionInner(uint64 Hash, FMaterialUniformExpression* UniformExpression, EMaterialValueType Type, const TCHAR* FormattedCode); int32 AddUniformExpression(FMaterialUniformExpression* UniformExpression, EMaterialValueType Type, const TCHAR* Format, ...); int32 AddUniformExpressionWithHash(uint64 BaseHash, FMaterialUniformExpression* UniformExpression, EMaterialValueType Type, const TCHAR* Format, ...); // 材质表达式. virtual int32 Sine(int32 X) override; virtual int32 Cosine(int32 X) override; virtual int32 Tangent(int32 X) override; virtual int32 Arcsine(int32 X) override; virtual int32 ArcsineFast(int32 X) override; virtual int32 Arccosine(int32 X) override; virtual int32 Floor(int32 X) override; virtual int32 Ceil(int32 X) override; virtual int32 Round(int32 X) override; virtual int32 Truncate(int32 X) override; virtual int32 Sign(int32 X) override; virtual int32 Frac(int32 X) override; virtual int32 Fmod(int32 A, int32 B) override; (......) }; ``` FHLSLMaterialTranslator实现了FMaterialCompiler的所有抽象接口,它的核心核心成员和接口如下: - FMaterial* Material:编译的目标材质。 - FMaterialCompilationOutput& MaterialCompilationOutput:编译后的结果。 - FString MaterialTemplate:待填充或填充后的MaterialTemplate.ush字符串。 - Translate():执行HLSL转译,将表达式转译成代码块保存到对应的属性槽中。 - GetMaterialShaderCode():将材质的宏、属性、表达式等数据填充到MaterialTemplate.ush并返回结果。 ## MaterialTemplate.ush MaterialTemplate.usf是材质shader模板,内涵大量%s的空缺和待替换的宏,它们由FHLSLMaterialTranslator::GetMaterialShaderCode负责填充。 ```c++ // Engine\Shaders\Private\MaterialTemplate.ush #include "/Engine/Private/SceneTexturesCommon.ush" #include "/Engine/Private/EyeAdaptationCommon.ush" #include "/Engine/Private/Random.ush" #include "/Engine/Private/SobolRandom.ush" #include "/Engine/Private/MonteCarlo.ush" #include "/Engine/Generated/UniformBuffers/Material.ush" #include "/Engine/Private/DepthOfFieldCommon.ush" #include "/Engine/Private/CircleDOFCommon.ush" #include "/Engine/Private/GlobalDistanceFieldShared.ush" #include "/Engine/Private/SceneData.ush" #include "/Engine/Private/HairShadingCommon.ush" ////////////////////////////////////////////////////////////////////////// //! Must match ESceneTextureId // 后处理属性宏. #define PPI_SceneColor 0 #define PPI_SceneDepth 1 #define PPI_DiffuseColor 2 #define PPI_SpecularColor 3 #define PPI_SubsurfaceColor 4 #define PPI_BaseColor 5 #define PPI_Specular 6 #define PPI_Metallic 7 #define PPI_WorldNormal 8 #define PPI_SeparateTranslucency 9 #define PPI_Opacity 10 #define PPI_Roughness 11 #define PPI_MaterialAO 12 #define PPI_CustomDepth 13 (......) ////////////////////////////////////////////////////////////////////////// // 待填充的宏定义. #define NUM_MATERIAL_TEXCOORDS_VERTEX %s #define NUM_MATERIAL_TEXCOORDS %s #define NUM_CUSTOM_VERTEX_INTERPOLATORS %s #define NUM_TEX_COORD_INTERPOLATORS %s // 顶点插值位置定义. %s // 文件引用和宏定义. #include "/Engine/Private/PaniniProjection.ush" #ifndef USE_DITHERED_LOD_TRANSITION #if USE_INSTANCING #ifndef USE_DITHERED_LOD_TRANSITION_FOR_INSTANCED #error "USE_DITHERED_LOD_TRANSITION_FOR_INSTANCED should have been defined" #endif #define USE_DITHERED_LOD_TRANSITION USE_DITHERED_LOD_TRANSITION_FOR_INSTANCED #else #ifndef USE_DITHERED_LOD_TRANSITION_FROM_MATERIAL #error "USE_DITHERED_LOD_TRANSITION_FROM_MATERIAL should have been defined" #endif #define USE_DITHERED_LOD_TRANSITION USE_DITHERED_LOD_TRANSITION_FROM_MATERIAL #endif #endif #ifndef USE_STENCIL_LOD_DITHER #define USE_STENCIL_LOD_DITHER USE_STENCIL_LOD_DITHER_DEFAULT #endif #define MATERIALBLENDING_ANY_TRANSLUCENT (MATERIALBLENDING_TRANSLUCENT || MATERIALBLENDING_ADDITIVE || MATERIALBLENDING_MODULATE) (......) // 材质的各类结构体. struct FMaterialAttributes { %s }; struct FPixelMaterialInputs { %s }; // 像素参数. struct FMaterialPixelParameters { #if NUM_TEX_COORD_INTERPOLATORS float2 TexCoords[NUM_TEX_COORD_INTERPOLATORS]; #endif half4 VertexColor; half3 WorldNormal; half3 WorldTangent; half3 ReflectionVector; half3 CameraVector; half3 LightVector; float4 SvPosition; float4 ScreenPosition; half UnMirrored; half TwoSidedSign; half3x3 TangentToWorld; #if USE_WORLDVERTEXNORMAL_CENTER_INTERPOLATION half3 WorldVertexNormal_Center; #endif float3 AbsoluteWorldPosition; float3 WorldPosition_CamRelative; float3 WorldPosition_NoOffsets; float3 WorldPosition_NoOffsets_CamRelative; half3 LightingPositionOffset; float AOMaterialMask; #if LIGHTMAP_UV_ACCESS float2 LightmapUVs; #endif #if USE_INSTANCING half4 PerInstanceParams; #endif uint PrimitiveId; #if TEX_COORD_SCALE_ANALYSIS FTexCoordScalesParams TexCoordScalesParams; #endif (.....) }; // 顶点参数. struct FMaterialVertexParameters { float3 WorldPosition; half3x3 TangentToWorld; #if USE_INSTANCING float4x4 InstanceLocalToWorld; float3 InstanceLocalPosition; float4 PerInstanceParams; uint InstanceId; uint InstanceOffset; #elif IS_MESHPARTICLE_FACTORY float4x4 InstanceLocalToWorld; #endif float4x4 PrevFrameLocalToWorld; float3 PreSkinnedPosition; float3 PreSkinnedNormal; #if GPU_SKINNED_MESH_FACTORY float3 PreSkinOffset; float3 PostSkinOffset; #endif half4 VertexColor; #if NUM_MATERIAL_TEXCOORDS_VERTEX float2 TexCoords[NUM_MATERIAL_TEXCOORDS_VERTEX]; #if ES3_1_PROFILE float2 TexCoordOffset; #endif #endif FMaterialParticleParameters Particle; uint PrimitiveId; (......) }; // 数据操作接口. MaterialFloat3x3 GetLocalToWorld3x3(uint PrimitiveId); MaterialFloat3x3 GetLocalToWorld3x3(); float3 GetObjectWorldPosition(FMaterialPixelParameters Parameters); float3 GetObjectWorldPosition(FMaterialTessellationParameters Parameters); (......) // 材质表达式接口. float MaterialExpressionDepthOfFieldFunction(float SceneDepth, int FunctionValueIndex); float2 MaterialExpressionGetAtlasUVs(FMaterialPixelParameters Parameters); float4 MaterialExpressionGetHairAuxilaryData(FMaterialPixelParameters Parameters); float3 MaterialExpressionGetHairColorFromMelanin(float Melanin, float Redness, float3 DyeColor); (......) // 材质属性查找. MaterialFloat4 ProcessMaterialColorTextureLookup(MaterialFloat4 TextureValue); MaterialFloat4 ProcessMaterialVirtualColorTextureLookup(MaterialFloat4 TextureValue); MaterialFloat4 ProcessMaterialExternalTextureLookup(MaterialFloat4 TextureValue); MaterialFloat4 ProcessMaterialLinearColorTextureLookup(MaterialFloat4 TextureValue); MaterialFloat ProcessMaterialGreyscaleTextureLookup(MaterialFloat TextureValue); (......) // 统一材质表达式. %s // 材质属性获取接口. half3 GetMaterialNormalRaw(FPixelMaterialInputs PixelMaterialInputs); half3 GetMaterialNormal(FMaterialPixelParameters Parameters, FPixelMaterialInputs PixelMaterialInputs); half3 GetMaterialTangentRaw(FPixelMaterialInputs PixelMaterialInputs); half3 GetMaterialTangent(FPixelMaterialInputs PixelMaterialInputs); half3 GetMaterialEmissiveRaw(FPixelMaterialInputs PixelMaterialInputs); half3 GetMaterialEmissive(FPixelMaterialInputs PixelMaterialInputs); half3 GetMaterialEmissiveForCS(FMaterialPixelParameters Parameters); { %s; } uint GetMaterialShadingModel(FPixelMaterialInputs PixelMaterialInputs); half3 GetMaterialBaseColorRaw(FPixelMaterialInputs PixelMaterialInputs); half3 GetMaterialBaseColor(FPixelMaterialInputs PixelMaterialInputs); half GetMaterialCustomData0(FMaterialPixelParameters Parameters) { %s; } half GetMaterialCustomData1(FMaterialPixelParameters Parameters) { %s; } half GetMaterialAmbientOcclusionRaw(FPixelMaterialInputs PixelMaterialInputs); half GetMaterialAmbientOcclusion(FPixelMaterialInputs PixelMaterialInputs); half2 GetMaterialRefraction(FPixelMaterialInputs PixelMaterialInputs); (......) // 计算材质参数接口. void CalcMaterialParametersEx( in out FMaterialPixelParameters Parameters, in out FPixelMaterialInputs PixelMaterialInputs, float4 SvPosition, float4 ScreenPosition, FIsFrontFace bIsFrontFace, float3 TranslatedWorldPosition, float3 TranslatedWorldPositionExcludingShaderOffsets); void CalcMaterialParameters( in out FMaterialPixelParameters Parameters, in out FPixelMaterialInputs PixelMaterialInputs, float4 SvPosition, FIsFrontFace bIsFrontFace); void CalcMaterialParametersPost( in out FMaterialPixelParameters Parameters, in out FPixelMaterialInputs PixelMaterialInputs, float4 SvPosition, FIsFrontFace bIsFrontFace); float ApplyPixelDepthOffsetToMaterialParameters(inout FMaterialPixelParameters MaterialParameters, FPixelMaterialInputs PixelMaterialInputs, out float OutDepth); (......) ``` MaterialTemplate.ush包含了大量的数据和接口,主要有几类: - 基础shader模块引用。 - 待填充的宏定义。 - 待填充的接口实现。 - 顶点、像素、材质属性等结构体定义。部分结构体待填充。 - 材质属性、数据处理、表达式、工具类接口定义。部分接口待填充。 # 材质编译流程 材质ShaderMap的编译入口在FMaterial的以下两个接口: - FMaterial::BeginCompileShaderMap - FMaterial::GetMaterialExpressionSource **编译不同类型的shader时,需要的数据不完全一样**: - GlobalShader:Shader_x.usf - MaterialShader:Shader_x.usf + MaterialTemplate_x.usf - MeshMaterialShader:Shader_x.usf + MaterialTemplate_x.usf + VertexFactory_x.usf