언리얼 렌더링 시스템 분석(09) - 머티리얼 시스템
🌐 원문 링크: 剖析虚幻渲染体系(09)- 材质体系 (cnblogs.com/timlly)
📅 원문 발행일: 2021-08-06 | ✍️ 저자: Timlly (00 / )💡 시리즈 분류:
Unreal Engine Rendering| 국내 업계 표준 용어 감수 및 수식/도해 복원 적용 완료
본 장에서는 재료에 관련된 기본 개념과 종류를 분석하고, 이들 간의 기본적인 관계와 사용법을 설명한다.
9.2.1 UMaterial
UMaterial은 엔진 레이어에 속하는 개념입니다. 이는 머티리얼 편집기에서 편집하는 uasset 리소스 파일에 해당하며 그리드에 적용하여 장면의 시각적 효과를 제어할 수 있습니다. 이는 UMaterialInterface를 상속하며 그 정의는 다음과 같습니다:
// Engine\Source\Runtime\Engine\Classes\Materials\MaterialInterface.h
// 머티리얼(Material)의 인터페이스 , 정의 머티리얼(Material)의 데이터 및 인터페이스 , 인터페이스 는 구현 또는 구현 의 인터페이스 .
class UMaterialInterface : public UObject, public IBlendableInterface, public IInterface_AssetUserData
{
// 회 테이블 산란 (구성(Configuration))
class USubsurfaceProfile* SubsurfaceProfile;
// 을(를) 활용하여 의 배리어/펜스 .
FRenderCommandFence ParentRefFence;
protected:
// Lightmass의 GI설정(Set).
struct FLightmassMaterialInterfaceSettings LightmassSettings;
// 텍스처(Texture)데이터 .
TArray<FMaterialTextureInfo> TextureStreamingData;
// 머티리얼(Material)리소스 의 을(를) 활용하여 데이터 리스트 .
TArray<UAssetUserData*> AssetUserData;
private:
// 의 Feature Level.
uint32 FeatureLevelsToForceCompile;
public:
//---- IInterface_AssetUserData인터페이스 ----
virtual void AddAssetUserData(UAssetUserData* InUserData) override;
virtual void RemoveUserDataOfClass(TSubclassOf<UAssetUserData> InUserDataClass) override;
virtual UAssetUserData* GetAssetUserDataOfClass(TSubclassOf<UAssetUserData> InUserDataClass) override;
//---- IInterface_AssetUserData인터페이스 ----
// 로 머티리얼(Material)의 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인터페이스 ----
// 탐색/조회(Find/Lookup)개 인스턴스 의 머티리얼(Material).
UMaterial* GetBaseMaterial();
// 페치/가져오기(Fetch)인스턴스 의 머티리얼(Material)
virtual class UMaterial* GetMaterial() PURE_VIRTUAL(UMaterialInterface::GetMaterial,return NULL;);
virtual const class UMaterial* GetMaterial() const PURE_VIRTUAL(UMaterialInterface::GetMaterial,return NULL;);
// 페치/가져오기(Fetch)인스턴스 의 머티리얼(Material)(동시성 )
virtual const class UMaterial* GetMaterial_Concurrent(TMicRecursionGuard RecursionGuard = TMicRecursionGuard()) const PURE_VIRTUAL(UMaterialInterface::GetMaterial_Concurrent,return NULL;);
// 머티리얼(Material)는 의 .
virtual bool IsDependent(UMaterialInterface* TestDependency);
virtual bool IsDependent_Concurrent(UMaterialInterface* TestDependency, TMicRecursionGuard RecursionGuard = TMicRecursionGuard());
// 페치/가져오기(Fetch)머티리얼(Material)의 을(를) 활용하여 렌더링(Render)의 FMaterialRenderProxy인스턴스 .
virtual class FMaterialRenderProxy* GetRenderProxy() const PURE_VIRTUAL(UMaterialInterface::GetRenderProxy,return NULL;);
(......)
// 페치/가져오기(Fetch)을(를) 활용하여 렌더링(Render)머티리얼(Material)의 텍스처(Texture)리스트 .
virtual void GetUsedTextures(TArray<UTexture*>& OutTextures, EMaterialQualityLevel::Type QualityLevel, bool bAllQualityLevels, ERHIFeatureLevel::Type FeatureLevel, bool bAllFeatureLevels) const
PURE_VIRTUAL(UMaterialInterface::GetUsedTextures,);
// 페치/가져오기(Fetch)을(를) 활용하여 렌더링(Render)머티리얼(Material)의 텍스처(Texture)리스트 및 .
virtual void GetUsedTexturesAndIndices(TArray<UTexture*>& OutTextures, TArray< TArray<int32> >& OutIndices, EMaterialQualityLevel::Type QualityLevel, ERHIFeatureLevel::Type FeatureLevel) const;
// 의 텍스처(Texture).
virtual void OverrideTexture(const UTexture* InTextureToOverride, UTexture* OverrideTexture, ERHIFeatureLevel::Type InFeatureLevel) PURE_VIRTUAL(UMaterialInterface::OverrideTexture, return;);
// 파라미터 의 기본값(Default)(의 )
virtual void OverrideVectorParameterDefault(const FHashedMaterialParameterInfo& ParameterInfo, const FLinearColor& Value, bool bOverride, ERHIFeatureLevel::Type FeatureLevel) PURE_VIRTUAL(UMaterialInterface::OverrideTexture, return;);
// 검사/감지(Detect)의 머티리얼(Material)플래그 , 만약 반환(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;);
// 페치/가져오기(Fetch)머티리얼(Material)의 렌더링(Render)텍스처(Texture), FeatureLevel/QualityLevel.
virtual FMaterialResource* GetMaterialResource(ERHIFeatureLevel::Type InFeatureLevel, EMaterialQualityLevel::Type QualityLevel = EMaterialQualityLevel::Num);
// 페치/가져오기(Fetch)정렬 단계 .
virtual bool GetGroupSortPriority(const FString& InGroupName, int32& OutSortPriority) const
PURE_VIRTUAL(UMaterialInterface::GetGroupSortPriority, return false;);
// 페치/가져오기(Fetch)머티리얼(Material)의 타입/유형 의 데이터 .
virtual void GetAllScalarParameterInfo(TArray<FMaterialParameterInfo>& OutParameterInfo, TArray<FGuid>& OutParameterIds) const
PURE_VIRTUAL(UMaterialInterface::GetAllScalarParameterInfo,return;);
virtual void GetAllVectorParameterInfo(TArray<FMaterialParameterInfo>& OutParameterInfo, TArray<FGuid>& OutParameterIds) const
PURE_VIRTUAL(UMaterialInterface::GetAllVectorParameterInfo,return;);
virtual void GetAllTextureParameterInfo(TArray<FMaterialParameterInfo>& OutParameterInfo, TArray<FGuid>& OutParameterIds) const
PURE_VIRTUAL(UMaterialInterface::GetAllTextureParameterInfo,return;);
virtual void GetAllRuntimeVirtualTextureParameterInfo(TArray<FMaterialParameterInfo>& OutParameterInfo, TArray<FGuid>& OutParameterIds) const
PURE_VIRTUAL(UMaterialInterface::GetAllRuntimeVirtualTextureParameterInfo, return;);
virtual void GetAllFontParameterInfo(TArray<FMaterialParameterInfo>& OutParameterInfo, TArray<FGuid>& OutParameterIds) const
PURE_VIRTUAL(UMaterialInterface::GetAllFontParameterInfo,return;);
// 페치/가져오기(Fetch)머티리얼(Material)의 타입/유형 의 데이터 .
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;);
// 페치/가져오기(Fetch)레벨 데이터 .
virtual int32 GetLayerParameterIndex(EMaterialParameterAssociation Association, UMaterialFunctionInterface * LayerFunction) const
PURE_VIRTUAL(UMaterialInterface::GetLayerParameterIndex, return INDEX_NONE;);
// 페치/가져오기(Fetch)에 의해 테이블 을(를) 활용하여 의 텍스처(Texture),함수 .
virtual TArrayView<UObject* const> GetReferencedTextures() const
PURE_VIRTUAL(UMaterialInterface::GetReferencedTextures,return TArrayView<UObject* const>(););
// 저장(Save)shader의 .
virtual void SaveShaderStableKeysInner(const class ITargetPlatform* TP, const struct FStableShaderKeyAndValue& SaveKeyVal)
PURE_VIRTUAL(UMaterialInterface::SaveShaderStableKeysInner, );
// 페치/가져오기(Fetch)머티리얼(Material)파라미터 정보 .
FMaterialParameterInfo GetParameterInfo(EMaterialParameterAssociation Association, FName ParameterName, UMaterialFunctionInterface* LayerFunction) const;
// 페치/가져오기(Fetch)머티리얼(Material)플래그 .
ENGINE_API FMaterialRelevance GetRelevance(ERHIFeatureLevel::Type InFeatureLevel) const;
ENGINE_API FMaterialRelevance GetRelevance_Concurrent(ERHIFeatureLevel::Type InFeatureLevel) const;
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
// 머티리얼(Material) 및 텍스처(Texture).
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<FGuid>& OutGuids) const;
virtual bool UpdateLightmassTextureTracking();
inline bool GetOverrideCastShadowAsMasked() const;
inline bool GetOverrideEmissiveBoost() const;
(......)
// 데이터 페치/가져오기(Fetch)인터페이스 .
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;
// 머티리얼(Material)의 .
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;
// 무시/스킵(Ignore)의 로직 ,는 로드 머티리얼(Material)을(를) 활용하여 의 텍스처(Texture)의 mip단계 .
virtual void SetForceMipLevelsToBeResident( bool OverrideForceMiplevelsToBeResident, bool bForceMiplevelsToBeResidentValue, float ForceDuration, int32 CinematicTextureGroups = 0, bool bFastResponse = false );
// 캐싱(Cache)머티리얼(Material)인터페이스 의 테이블 .
static void RecacheAllMaterialUniformExpressions(bool bRecreateUniformBuffer);
virtual void RecacheUniformExpressions(bool bRecreateUniformBuffer) const;
// 초기화(Initialize)의 기본값(Default)머티리얼(Material).
static void InitDefaultMaterials();
virtual bool IsPropertyActive(EMaterialProperty InProperty) const;
static uint32 GetFeatureLevelsToCompileForAllMaterials()
// 반환(Return)을(를) 활용하여 텍스처(Texture)좌표 의 개수 ,로써 및 포인트 데이터 는 셰이딩 내에서 을(를) 활용하여 .
void AnalyzeMaterialProperty(EMaterialProperty InProperty, int32& OutNumTextureCoordinates, bool& bOutRequiresVertexData);
// 순회(Traverse/Iterate)의 FeatureLevel, 로써 .
template <typename FunctionType>
static void IterateOverActiveFeatureLevels(FunctionType InHandler);
// 머티리얼(Material)샘플러(Sampler)타입/유형 의 캐싱(Cache)의 UEnum타입/유형 정보 .
static UEnum* GetSamplerTypeEnum();
bool UseAnyStreamingTexture() const;
bool HasTextureStreamingData() const;
const TArray<FMaterialTextureInfo>& GetTextureStreamingData() const;
FORCEINLINE TArray<FMaterialTextureInfo>& GetTextureStreamingData();
// 텍스처(Texture)인터페이스 .
bool FindTextureStreamingDataIndexRange(FName TextureName, int32& LowerIndex, int32& HigherIndex) const;
void SetTextureStreamingData(const TArray<FMaterialTextureInfo>& InTextureStreamingData);
// 반환(Return)텍스처(Texture)의 (LocalSpace Unit / Texture), 을(를) 활용하여 텍스처(Texture)모멘트(Moment).
virtual float GetTextureDensity(FName TextureName, const struct FMeshUVChannelInfo& UVChannelData) const;
// 저장(Save).
virtual void PreSave(const class ITargetPlatform* TargetPlatform) override;
// 에 따라 텍스처(Texture)데이터 정렬 ,로써 가속 검색 . 오직 정렬 .
void SortTextureStreamingData(bool bForceSort, bool bFinalSort);
protected:
uint32 GetFeatureLevelsToCompileForRendering() const;
void UpdateMaterialRenderProxy(FMaterialRenderProxy& Proxy);
private:
static void PostLoadDefaultMaterials();
// 머티리얼(Material)샘플러(Sampler)타입/유형 의 캐싱(Cache)의 UEnum타입/유형 정보 .
static UEnum* SamplerTypeEnum;
};
// Engine\Source\Runtime\Engine\Classes\Materials\Material.h
// 머티리얼(Material), 개 머티리얼(Material)리소스 .
class UMaterial : public UMaterialInterface
{
// 머티리얼(Material).
class UPhysicalMaterial* PhysMaterial;
class UPhysicalMaterialMask* PhysMaterialMask;
class UPhysicalMaterial* PhysicalMaterialMap[EPhysicalMaterialMaskColor::MAX];
// 머티리얼(Material).
FScalarMaterialInput Metallic;
FScalarMaterialInput Specular;
FScalarMaterialInput Anisotropy;
FVectorMaterialInput Normal;
FVectorMaterialInput Tangent;
FColorMaterialInput EmissiveColor;
#if WITH_EDITORONLY_DATA
// 투과 .
FScalarMaterialInput Opacity;
FScalarMaterialInput OpacityMask;
#endif
TEnumAsByte<enum EMaterialDomain> MaterialDomain;
TEnumAsByte<enum EBlendMode> BlendMode;
TEnumAsByte<enum EDecalBlendMode> DecalBlendMode;
TEnumAsByte<enum EMaterialDecalResponse> MaterialDecalResponse;
TEnumAsByte<enum EMaterialShadingModel> ShadingModel;
UPROPERTY(AssetRegistrySearchable)
FMaterialShadingModelField ShadingModels;
public:
// 머티리얼(Material).
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;
// 머티리얼(Material)플래그 .
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;
// 반투명(Translucent).
TEnumAsByte<enum ETranslucencyLightingMode> 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;
// 에 따라 스크린 스페이스거칠기(Roughness).
uint8 bNormalCurvatureToRoughness : 1;
uint8 AllowTranslucentCustomDepthWrites : 1;
uint8 Wireframe : 1;
// 셰이딩 주파수(Frequency).
TEnumAsByte<EMaterialShadingRate> 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<enum EBlendableLocation> BlendableLocation;
uint8 BlendableOutputAlpha : 1;
uint8 bEnableStencilTest : 1;
TEnumAsByte<EMaterialStencilCompare> StencilCompare;
uint8 StencilRefValue = 0;
TEnumAsByte<enum ERefractionMode> RefractionMode;
int32 BlendablePriority;
uint8 bIsBlendable : 1;
uint32 UsageFlagWarnings;
float RefractionDepthBias;
FGuid StateId;
float MaxDisplacement;
// 렌더링(Render)페치/가져오기(Fetch)파라미터 ,테이블 개 머티리얼(Material)까지 렌더링(Render)의 FMaterialRenderProxy.
class FDefaultMaterialInstance* DefaultMaterialInstance;
#if WITH_EDITORONLY_DATA
// 파라미터 .
TMap<FName, TArray<UMaterialExpression*> > EditorParameters;
// 머티리얼(Material). 모델 의 데이터 .
class UMaterialGraph* MaterialGraph;
#endif
private:
// 로부터 시퀀스 의 머티리얼(Material)리소스 . 에 의해 스레드(Thread)의 PostLoad처리(Process).
TArray<FMaterialResource> LoadedMaterialResources;
// 을(를) 활용하여 렌더링(Render)머티리얼(Material)의 리소스 리스트 .
TArray<FMaterialResource*> MaterialResources;
#if WITH_EDITOR
// 캐싱(Cache) 또는 의 머티리얼(Material)리소스 .
TMap<const class ITargetPlatform*, TArray<FMaterialResource*>> 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<FAssetRegistryTag>& OutTags) const override;
//~ End UObject Interface
// 데이터 페치/가져오기(Fetch)인터페이스 .
bool IsDefaultMaterial() const;
void ReleaseResources();
bool IsUsageFlagDirty(EMaterialUsage Usage);
bool IsCompilingOrHadCompileError(ERHIFeatureLevel::Type InFeatureLevel);
(......)
private:
// 의 페치/가져오기(Fetch)머티리얼(Material)데이터 인터페이스 .
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<UObject* const> GetReferencedTextures() const override final;
(......)
};UMaterialInterface는 공통 재질 속성 및 인터페이스 집합을 정의하는 기본 추상 클래스입니다. UMaterialInterface에 의해 선언된 주요 데이터는 다음과 같습니다:
USubsurfaceProfile* SubsurfaceProfile: 피부, 옥 및 기타 재질에 일반적으로 사용되는 표면 아래 산란 프로파일입니다.
TArray<UAssetUserData*> AssetUserData: 사용자 데이터, 여러 데이터를 저장할 수 있습니다.
FLLightmassMaterialInterfaceSettings LightmassSettings: 오프라인 GI 데이터.
TArray TextureStreamingData: 텍스처 스트리밍 데이터.
UMaterialInterface의 하위 클래스는 UMaterial뿐만 아니라 UMaterialInstance도 있습니다(나중에 분석됨).
UMaterial은 머티리얼에 필요한 모든 데이터 및 작업 인터페이스를 정의하고 다른 관련 유형에 대한 링크를 여는 역할을 담당합니다. 핵심 데이터는 다음과 같습니다.
TArray<FMaterialResource*> MaterialResources: 머티리얼 렌더링 리소스. 재료에는 여러 렌더링 리소스 인스턴스가 있을 수 있습니다.
FDefaultMaterialInstance* DefaultMaterialInstance: FMaterialRenderProxy에서 상속된 기본 재료 렌더링 프록시입니다.
UPhysicalMaterial* PhysMaterial: 물리적 재질.
TArray LoadedMaterialResources: 로드된 머티리얼 리소스, 일반적으로 게임 스레드에 의해 디스크에서 로드되고 직렬화됩니다.
재료에 대한 다양한 속성 및 태그.
그 중 FMaterialResource는 렌더링 레이어의 유형과 데이터에 속하는 렌더링 재료의 리소스입니다. 이는 다음 장에서 분석될 것입니다.
UMaterial은 머티리얼 에디터에 해당하는 리소스이며, 우리가 가장 자주 접하게 되는 객체이기도 합니다:

우리가 가장 자주 접하게 되는 머티리얼 에디터는 UMaterial 인스턴스를 편집합니다. 왼쪽 속성 패널의 옵션은 UMaterial 선언과 일치합니다.
UMaterial에는 유일한 특수 하위 클래스인 UPreviewMaterial이 있는데, 이는 재질 편집기의 왼쪽 상단 모서리에 있는 미리보기 창, 브라우저의 재질 썸네일 등과 같은 편집기 관련 재질 미리보기에 주로 사용됩니다.
9.2.2 UMaterialInstance
UMaterialInstance는 머티리얼 인스턴스이며 단독으로 존재할 수 없습니다. UMaterialInterface 유형의 상위 클래스에 의존해야 합니다. 즉, 상위 클래스는 UMaterialInterface의 하위 클래스일 수 있지만 최상위 상위 클래스는 UMaterial이어야 합니다. 이는 다음과 같이 정의됩니다:
class UMaterialInstance : public UMaterialInterface
{
// 머티리얼(Material).
class UPhysicalMaterial* PhysMaterial;
class UPhysicalMaterial* PhysicalMaterialMap[EPhysicalMaterialMaskColor::MAX];
// 머티리얼(Material).
class UMaterialInterface* Parent;
// 렌더링(Render)페치/가져오기(Fetch)파라미터 ,테이블 개 머티리얼(Material)인스턴스 의 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<EBlendMode> BlendMode;
float OpacityMaskClipValue;
FMaterialShadingModelField ShadingModels;
// Parent의 타입/유형 의 데이터 .
TArray<struct FScalarParameterValue> ScalarParameterValues;
TArray<struct FVectorParameterValue> VectorParameterValues;
TArray<struct FTextureParameterValue> TextureParameterValues;
TArray<struct FRuntimeVirtualTextureParameterValue> RuntimeVirtualTextureParameterValues;
TArray<struct FFontParameterValue> FontParameterValues;
struct FMaterialInstanceBasePropertyOverrides BasePropertyOverrides;
(......)
private:
FStaticParameterSet StaticParameters;
FMaterialCachedParameters CachedLayerParameters;
TArray<UObject*> CachedReferencedTextures;
// 로드 의 머티리얼(Material)리소스 .
TArray<FMaterialResource> LoadedMaterialResources;
TArray<FMaterialResource*> 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<FMaterialShaderMap*>& OutShaderMaps);
void GetAllParametersOfType(EMaterialParameterType Type, TArray<FMaterialParameterInfo>& OutParameterInfo, TArray<FGuid>& OutParameterIds) const;
(......)
protected:
void CopyMaterialUniformParametersInternal(UMaterialInterface* Source);
bool UpdateParameters();
ENGINE_API void SetParentInternal(class UMaterialInterface* NewParent, bool RecacheShaders);
(......)
// 초기화(Initialize)머티리얼(Material)인스턴스 의 리소스 .
ENGINE_API void InitResources();
// 캐싱(Cache)리소스 .
void CacheResourceShadersForRendering();
void CacheResourceShadersForRendering(FMaterialResourceDeferredDeletionArray& OutResourcesToFree);
void CacheShadersForResources(...);
void DeleteDeferredResources(FMaterialResourceDeferredDeletionArray& ResourcesToFree);
ENGINE_API void CopyMaterialInstanceParameters(UMaterialInterface* Source);
(......)
};UMaterialInstance는 UMaterial과 다릅니다. 상위 인스턴스에 연결되어야 하며 최상위 상위는 UMaterial 인스턴스여야 합니다. 이는 UMaterial 매개변수의 작은 부분만 다룰 수 있으며 일반적으로 별도로 생성되지 않지만 두 하위 클래스 UMaterialInstanceConstant 및 UMaterialInstanceDynamic으로 생성됩니다.
// Engine\Source\Runtime\Engine\Classes\Materials\MaterialInstanceConstant.h
// 머티리얼(Material)인스턴스
class UMaterialInstanceConstant : public UMaterialInstance
{
// 데이터
#if WITH_EDITOR
friend class UMaterialInstanceConstantFactoryNew;
friend class UMaterialEditorInstanceConstant;
virtual ENGINE_API void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif
class UPhysicalMaterialMask* PhysMaterialMask;
// Begin UMaterialInterface interface.
virtual UPhysicalMaterialMask* GetPhysicalMaterialMask() const override;
// End UMaterialInterface interface.
float K2_GetScalarParameterValue(FName ParameterName);
class UTexture* K2_GetTextureParameterValue(FName ParameterName);
FLinearColor K2_GetVectorParameterValue(FName ParameterName);
ENGINE_API void PostLoad();
(......)
};
// Engine\Source\Runtime\Engine\Classes\Materials\MaterialInstanceDynamic.h
// 동적 머티리얼(Material)인스턴스
class ENGINE_API UMaterialInstanceDynamic : public UMaterialInstance
{
// 생성(Create)동적 인스턴스 , 인스턴스 .
static UMaterialInstanceDynamic* Create(class UMaterialInterface* ParentMaterial, class UObject* InOuter);
static UMaterialInstanceDynamic* Create( class UMaterialInterface* ParentMaterial, class UObject* InOuter, FName Name );
// 로부터 머티리얼(Material) 또는 인스턴스 레벨 단계 Uniform파라미터 ((scalar, vector and texture).
void CopyMaterialUniformParameters(UMaterialInterface* Source);
void CopyInterpParameters(UMaterialInstance* Source);
void CopyParameterOverrides(UMaterialInstance* MaterialInstance);
void CopyScalarAndVectorParameters(const UMaterialInterface& SourceMaterialToCopyFrom, ERHIFeatureLevel::Type FeatureLevel);
// 파라미터 .
void ClearParameterValues();
// 초기화(Initialize).
bool InitializeScalarParameterAndGetIndex(const FName& ParameterName, float Value, int32& OutParameterIndex);
bool InitializeVectorParameterAndGetIndex(const FName& ParameterName, const FLinearColor& Value, int32& OutParameterIndex);
// 데이터 설정(Set)인터페이스 .
void SetScalarParameterValue(FName ParameterName, float Value);
void SetScalarParameterValueByInfo(const FMaterialParameterInfo& ParameterInfo, float Value);
bool SetScalarParameterByIndex(int32 ParameterIndex, float Value);
bool SetVectorParameterByIndex(int32 ParameterIndex, const FLinearColor& Value);
void SetTextureParameterValue(FName ParameterName, class UTexture* Value);
void SetTextureParameterValueByInfo(const FMaterialParameterInfo& ParameterInfo, class UTexture* Value);
void SetVectorParameterValue(FName ParameterName, FLinearColor Value);
void SetVectorParameterValueByInfo(const FMaterialParameterInfo& ParameterInfo, FLinearColor Value);
void SetFontParameterValue(const FMaterialParameterInfo& ParameterInfo, class UFont* FontValue, int32 FontPage);
// 데이터 페치/가져오기(Fetch)인터페이스 .
float K2_GetScalarParameterValue(FName ParameterName);
float K2_GetScalarParameterValueByInfo(const FMaterialParameterInfo& ParameterInfo);
class UTexture* K2_GetTextureParameterValue(FName ParameterName);
class UTexture* K2_GetTextureParameterValueByInfo(const FMaterialParameterInfo& ParameterInfo);
FLinearColor K2_GetVectorParameterValue(FName ParameterName);
FLinearColor K2_GetVectorParameterValueByInfo(const FMaterialParameterInfo& ParameterInfo);
void K2_InterpolateMaterialInstanceParams(UMaterialInstance* SourceA, UMaterialInstance* SourceB, float Alpha);
void K2_CopyMaterialInstanceParameters(UMaterialInterface* Source, bool bQuickParametersOnly = false);
virtual bool HasOverridenBaseProperties()const override{ return false; }
virtual float GetOpacityMaskClipValue() const override;
virtual bool GetCastDynamicShadowAsMasked() const override;
virtual FMaterialShadingModelField GetShadingModels() const override;
virtual bool IsShadingModelFromMaterialExpression() const override;
virtual EBlendMode GetBlendMode() const override;
virtual bool IsTwoSided() const override;
virtual bool IsDitheredLODTransition() const override;
virtual bool IsMasked() const override;
// 를 위해 매핑 까지 의 텍스처(Texture)데이터 , 필수: 개 의 텍스처(Texture).
TMap<FName, TArray<FName> > RenamedTextures;
// 페치/가져오기(Fetch)텍스처(Texture).
virtual float GetTextureDensity(FName TextureName, const struct FMeshUVChannelInfo& UVChannelData) const override;
};UMaterialInstanceConstant는 이름에서 알 수 있듯이 고정된 머티리얼 인스턴스로, 편집자가 미리 만들고 편집한 머티리얼 인스턴스 리소스에 사용됩니다.

머티리얼 인스턴스 에디터 개요. 오른쪽 중앙의 머티리얼 프로퍼티 오버라이드(Material Property Overrides)는 상대적으로 제한적인 머티리얼 인스턴스로 편집 및 오버라이드할 수 있는 속성을 보여줍니다.
UMaterialInstanceConstant는 런타임 시 머티리얼 매개변수 수정으로 인한 재컴파일을 방지하기 위해 탄생했으며, 내부적으로 제한된 데이터 적용 범위도 이로 인해 발생합니다. 재컴파일 없이 머티리얼에 대한 일반적인 수정은 지원되지 않으므로 인스턴스는 미리 정의된 머티리얼 매개변수의 값만 변경할 수 있습니다. 여기의 매개변수는 재료 편집기에서 정의된 고유한 이름, 유형 및 기본값을 가진 정적 정의입니다. 게다가 런타임 코드(비에디터 코드)에서는 UMaterialInstanceConstant 인스턴스의 머티리얼 속성을 변경할 수 없다는 점을 분명히 알아야 합니다. UMaterialInstanceConstant에는 랜드스케이프 렌더링 전용 ULandscapeMaterialInstanceConstant의 하위 클래스도 있습니다.
UMaterialInstanceDynamic은 UMaterialInstanceConstant와 다릅니다. 런타임 코드에서 머티리얼 속성을 동적으로 생성 및 수정하는 기능을 제공하며, 머티리얼 재컴파일도 발생하지 않습니다. UE의 내장 코드에는 대량의 UMaterialInstanceDynamic 생성, 설정 및 렌더링 코드가 포함되어 있습니다. 사용 사례는 다음과 같습니다.
// Engine\Source\Runtime\Engine\Private\Components\DecalComponent.cpp
class UMaterialInstanceDynamic* UDecalComponent::CreateDynamicMaterialInstance()
{
// 생성(Create)동적 인스턴스 , 내에서 DecalMaterial는 동적 인스턴스 의 , 에 의해 을(를) 활용하여 UDecalComponent의 머티리얼(Material).
UMaterialInstanceDynamic* Instance = UMaterialInstanceDynamic::Create(DecalMaterial, this);
// 의 머티리얼(Material).
SetDecalMaterial(Instance);
return Instance;
}UMaterialInstances 간의 상속 관계는 임의의 트리 구조일 수 있으며 이론적으로 상속 수준에는 제한이 없습니다(그러나 상속 수준이 너무 많으면 유지 관리가 어려워지고 운영 효율성이 떨어집니다). 최상위 상위 클래스는 UMaterial 인스턴스여야 하며, 보조 및 후속 상위 클래스는 UMaterialInstance 인스턴스여야 합니다. 아래 그림은 머티리얼 인스턴스 에디터에 표시되는 4레벨 계층 구조입니다:

레벨 4 머티리얼 인스턴스 레벨. 최상위 수준은 UMaterial 인스턴스이고 아래 세 수준은 UMaterialInstance 인스턴스입니다.
최상위 또는 중간 레이어의 상위 클래스를 빈 것으로 설정하려고 하면 UE는 이를 허용할 수 있지만 머티리얼의 렌더링 효과는 비정상적입니다.

9.2.3 FMaterialRenderProxy
우리는 프리미티브, 그리드, 광원과 같은 장면 유형이 게임 스레드와 렌더링 스레드로 표현되며 머티리얼도 예외는 아니라는 것을 알고 있습니다. 게임 스레드의 대표로서 UMaterialInterface에 해당하는 렌더링 스레드는 FMaterialRenderProxy입니다. FMaterialRenderProxy는 게임 스레드가 나타내는 데이터를 수신한 다음 처리 및 렌더링을 위해 렌더러에 전달하는 역할을 담당합니다. FMaterialRenderProxy는 다음과 같이 정의됩니다.
// Engine\Source\Runtime\Engine\Public\MaterialShared.h
class FMaterialRenderProxy : public FRenderResource
{
public:
// 캐싱(Cache)데이터 .
mutable FUniformExpressionCacheContainer UniformExpressionCache;
mutable FImmutableSamplerState ImmutableSamplerState;
// /함수 .
ENGINE_API FMaterialRenderProxy();
ENGINE_API virtual ~FMaterialRenderProxy();
// 계산/산출(Calculate)테이블 저장(Store)까지 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;
// 반환(Return)유효(Valid)의 FMaterial인스턴스 .
const class FMaterial* GetMaterial(ERHIFeatureLevel::Type InFeatureLevel) const;
// 탐색/조회(Find/Lookup)을(를) 활용하여 렌더링(Render)FMaterialRenderProxy의 FMaterial인스턴스 .
virtual const FMaterial& GetMaterialWithFallback(ERHIFeatureLevel::Type InFeatureLevel, const FMaterialRenderProxy*& OutFallbackMaterialRenderProxy) const = 0;
virtual FMaterial* GetMaterialNoFallback(ERHIFeatureLevel::Type InFeatureLevel) const { return NULL; }
// 페치/가져오기(Fetch)의 UMaterialInterface인스턴스 .
virtual UMaterialInterface* GetMaterialInterface() const { return NULL; }
// 페치/가져오기(Fetch)머티리얼(Material)의 .
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;
// 페치/가져오기(Fetch)정적 의 머티리얼(Material)렌더링(Render)테이블 의 매핑 테이블 .
ENGINE_API static const TSet<FMaterialRenderProxy*>& 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;
// 머티리얼(Material)플래그 .
mutable int8 MarkedForGarbageCollection : 1;
mutable int8 DeletedFlag : 1;
mutable int8 ReleaseResourceFlag : 1;
mutable int8 HasVirtualTextureCallbacks : 1;
// 의 머티리얼(Material)렌더링(Render)테이블 . 오직 렌더링(Render)스레드(Thread). 을(를) 활용하여 의 셰이딩 매핑 까지 렌더링(Render)을(를) 활용하여 의 머티리얼(Material).
ENGINE_API static TSet<FMaterialRenderProxy*> MaterialRenderProxyMap;
ENGINE_API static TSet<FMaterialRenderProxy*> DeferredUniformExpressionCacheRequests;
};FMaterialRenderProxy는 정적 전역 머티리얼 렌더링 프록시 매핑 테이블과 FMaterial 렌더링 인스턴스를 얻기 위한 인터페이스를 정의하는 추상 클래스입니다. 특정 논리는 하위 클래스로 완성되며 해당 하위 클래스는 다음과 같습니다.
FDefaultMaterialInstance: UMaterial의 기본 대표 인스턴스를 렌더링합니다.
FMaterialInstanceResource: UMaterialInstance 인스턴스의 대표를 렌더링합니다.
FColoredMaterialRenderProxy: 재료 색상 벡터 매개변수를 재정의하는 재료 렌더링 대표입니다.
FLandscapeMaskMaterialRenderProxy: 풍경 마스크 재질 렌더링 대표.
FLightmassMaterialProxy: 라이트매스 머티리얼 렌더링 프록시.
-......
우리는 다음과 같이 정의된 두 가지 중요한 하위 클래스인 FDefaultMaterialInstance 및 FMaterialInstanceResource에 중점을 둡니다.
// Engine\Source\Runtime\Engine\Private\Materials\Material.cpp
// 을(를) 활용하여 렌더링(Render)UMaterial의 기본값(Default)렌더링(Render)테이블 , 기본값(Default)의 파라미터 저장(Store)FMaterialUniformExpressionXxxParameter객체/오브젝트 , 리소스 을(를) 활용하여 저장(Store) 내에서 의 컬러 .
class FDefaultMaterialInstance : public FMaterialRenderProxy
{
public:
// 스레드(Thread)소멸 인터페이스 .
void GameThread_Destroy()
{
FDefaultMaterialInstance* Resource = this;
ENQUEUE_RENDER_COMMAND(FDestroyDefaultMaterialInstanceCommand)(
[Resource](FRHICommandList& RHICmdList)
{
delete Resource;
});
}
// FMaterialRenderProxy interface.
// 페치/가져오기(Fetch)머티리얼(Material)인터페이스 .
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);
}
// 페치/가져오기(Fetch)의 UMaterialInterface인터페이스 .
virtual UMaterialInterface* GetMaterialInterface() const override
{
return Material;
}
// 페치/가져오기(Fetch)벡터 의 파라미터 .
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);
}
}
// 페치/가져오기(Fetch)스칼라 의 파라미터 .
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);
}
}
// 페치/가져오기(Fetch)텍스처(Texture)의 파라미터 .
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:
// 페치/가져오기(Fetch)의 머티리얼(Material)렌더링(Render).
FMaterialRenderProxy& GetFallbackRenderProxy() const
{
return *(UMaterial::GetDefaultMaterial(Material->MaterialDomain)->GetRenderProxy());
}
// 의 머티리얼(Material)인스턴스 .
UMaterial* Material;
};
// Engine\Source\Runtime\Engine\Private\Materials\MaterialInstanceSupport.h
// 렌더링(Render)UMaterialInstance의 머티리얼(Material)리소스 .
class FMaterialInstanceResource: public FMaterialRenderProxy
{
public:
// 저장(Store)머티리얼(Material)인스턴스 의 및 의 페어링(Pairing).
template <typename ValueType>
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.
// 페치/가져오기(Fetch)머티리얼(Material)렌더링(Render)리소스 .
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;
// 페치/가져오기(Fetch)머티리얼(Material)의 .
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);
}
// 갱신(Update)파라미터 .
template <typename ValueType>
void RenderThread_UpdateParameter(const FHashedMaterialParameterInfo& ParameterInfo, const ValueType& Value )
{
LLM_SCOPE(ELLMTag::MaterialInstance);
InvalidateUniformExpressionCache(false);
TArray<TNamedParameter<ValueType> >& ValueArray = GetValueArray<ValueType>();
const int32 ParameterCount = ValueArray.Num();
for (int32 ParameterIndex = 0; ParameterIndex < ParameterCount; ++ParameterIndex)
{
TNamedParameter<ValueType>& Parameter = ValueArray[ParameterIndex];
if (Parameter.Info == ParameterInfo)
{
Parameter.Value = Value;
return;
}
}
TNamedParameter<ValueType> NewParameter;
NewParameter.Info = ParameterInfo;
NewParameter.Value = Value;
ValueArray.Add(NewParameter);
}
// 탐색/조회(Find/Lookup)의 파라미터 .
template <typename ValueType>
const ValueType* RenderThread_FindParameterByName(const FHashedMaterialParameterInfo& ParameterInfo) const
{
const TArray<TNamedParameter<ValueType> >& ValueArray = GetValueArray<ValueType>();
const int32 ParameterCount = ValueArray.Num();
for (int32 ParameterIndex = 0; ParameterIndex < ParameterCount; ++ParameterIndex)
{
const TNamedParameter<ValueType>& Parameter = ValueArray[ParameterIndex];
if (Parameter.Info == ParameterInfo)
{
return &Parameter.Value;
}
}
return NULL;
}
private:
template <typename ValueType> TArray<TNamedParameter<ValueType> >& GetValueArray();
// 머티리얼(Material)인스턴스 의 .
UMaterialInterface* Parent;
// 스레드(Thread)의 .
UMaterialInterface* GameThreadParent;
// 의 머티리얼(Material)인스턴스 .
UMaterialInstance* Owner;
// 타입/유형 의 파라미터 리스트 .
TArray<TNamedParameter<FLinearColor> > VectorParameterArray;
TArray<TNamedParameter<float> > ScalarParameterArray;
TArray<TNamedParameter<const UTexture*> > TextureParameterArray;
TArray<TNamedParameter<const URuntimeVirtualTexture*> > RuntimeVirtualTextureParameterArray;
};FMaterialRenderProxy는 게임 스레드와 렌더링 스레드 모두에서 처리된다는 점에 유의해야 합니다. 데이터 액세스와 이들 간의 인터페이스 호출에 세심한 주의를 기울여야 합니다. GameThread가 있는 것은 게임 스레드 전용이고, RenderThread가 있는 것은 렌더링 스레드 전용입니다. 특별한 지침이 없으면 일반적으로(절대적으로는 아님) 렌더링 스레드에 사용됩니다. 호출해서는 안 되는 인터페이스나 실수로 데이터에 액세스하면 경쟁 조건이 발생하여 무작위 충돌이 발생하고 디버깅이 기하급수적으로 어려워집니다.
9.2.4 FMaterial, FMaterialResource
FMaterial에는 3가지 기능이 있습니다.
재료 간 컴파일 프로세스를 나타내고 확장성 후크(CompileProperty 등)를 제공합니다.
재료 데이터를 렌더러에 전달하고 함수를 사용하여 재료 속성에 액세스합니다.
캐시된 셰이더 맵과 비동기 셰이더 컴파일에 필요한 컴파일의 기타 임시 출력을 저장합니다.
FMaterial의 정의는 다음과 같습니다.
// 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();
// 캐싱(Cache)shader.
ENGINE_API bool CacheShaders(EShaderPlatform Platform, const ITargetPlatform* TargetPlatform = nullptr);
ENGINE_API bool CacheShaders(const FMaterialShaderMapId& ShaderMapId, EShaderPlatform Platform, const ITargetPlatform* TargetPlatform = nullptr);
// 는 캐싱(Cache)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();
// 머티리얼(Material).
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; }
(......)
// 는 저장(Save)까지 .
virtual bool IsPersistent() const = 0;
// 페치/가져오기(Fetch)머티리얼(Material)인스턴스 .
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<const FMaterialTextureParameterInfo> GetUniformTextureExpressions(EMaterialTextureParameterType Type) const;
ENGINE_API TArrayView<const FMaterialVectorParameterInfo> GetUniformVectorParameterExpressions() const;
ENGINE_API TArrayView<const FMaterialScalarParameterInfo> GetUniformScalarParameterExpressions() const;
inline TArrayView<const FMaterialTextureParameterInfo> GetUniform2DTextureExpressions() const { return GetUniformTextureExpressions(EMaterialTextureParameterType::Standard2D); }
inline TArrayView<const FMaterialTextureParameterInfo> GetUniformCubeTextureExpressions() const { return GetUniformTextureExpressions(EMaterialTextureParameterType::Cube); }
inline TArrayView<const FMaterialTextureParameterInfo> GetUniform2DArrayTextureExpressions() const { return GetUniformTextureExpressions(EMaterialTextureParameterType::Array2D); }
inline TArrayView<const FMaterialTextureParameterInfo> GetUniformVolumeTextureExpressions() const { return GetUniformTextureExpressions(EMaterialTextureParameterType::Volume); }
inline TArrayView<const FMaterialTextureParameterInfo> 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;
// 머티리얼(Material)플래그 .
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<FMaterialShaderMap> ShaderMap = GameThreadShaderMap;
TRefCountPtr<FMaterial> Material = this;
// 스레드(Thread)의 shader map설정(Set)까지 렌더링(Render)스레드(Thread).
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<FMaterialShaderMap>& InMaterialShaderMap);
ENGINE_API virtual void AddReferencedObjects(FReferenceCollector& Collector);
virtual TArrayView<UObject* const> GetReferencedTextures() const = 0;
// 페치/가져오기(Fetch)shader/shader pipeline.
template<typename ShaderType>
TShaderRef<ShaderType> GetShader(FVertexFactoryType* VertexFactoryType, const typename ShaderType::FPermutationDomain& PermutationVector, bool bFatalIfMissing = true) const;
template <typename ShaderType>
TShaderRef<ShaderType> GetShader(FVertexFactoryType* VertexFactoryType, int32 PermutationId = 0, bool bFatalIfMissing = true) const;
ENGINE_API FShaderPipelineRef GetShaderPipeline(class FShaderPipelineType* ShaderPipelineType, FVertexFactoryType* VertexFactoryType, bool bFatalIfNotFound = true) const;
// 머티리얼(Material)인터페이스 .
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<FShaderType*>& OutShaderTypes, TArray<const FShaderPipelineType*>& OutShaderPipelineTypes, TArray<FVertexFactoryType*>& OutVFTypes) const;
bool GetLoadedCookedShaderMapId() const;
private:
// 스레드(Thread) 및 렌더링(Render)스레드(Thread)의 shader map.
TRefCountPtr<FMaterialShaderMap> GameThreadShaderMap;
TRefCountPtr<FMaterialShaderMap> 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<class FMaterialShaderMap>& OutShaderMap,
const ITargetPlatform* TargetPlatform = nullptr);
void SetupMaterialEnvironment(
EShaderPlatform Platform,
const FShaderParametersMetadata& InUniformBufferStruct,
const FUniformExpressionSet& InUniformExpressionSet,
FShaderCompilerEnvironment& OutEnvironment
) const;
ENGINE_API TShaderRef<FShader> GetShader(class FMeshMaterialShaderType* ShaderType, FVertexFactoryType* VertexFactoryType, int32 PermutationId, bool bFatalIfMissing = true) const;
};위에서 볼 수 있듯이 FMaterial은 Shader, VertexFactory, ShaderPipeline, ShaderMap 및 기타 데이터와 작업 인터페이스의 대규모 컬렉션이며 이러한 데이터의 배포 센터입니다. 그러나 이는 추상 상위 클래스일 뿐이며 하위 클래스에서 특정 기능을 구현해야 합니다. 유일한 하위 클래스는 FMaterialResource입니다.
// 구현 FMaterial의 인터페이스 , 을(를) 활용하여 렌더링(Render)UMaterial 또는 UMaterialInstance.
class FMaterialResource : public FMaterial
{
public:
ENGINE_API FMaterialResource();
ENGINE_API virtual ~FMaterialResource();
// 설정(Set)머티리얼(Material).
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;
(......)
// 머티리얼(Material)인터페이스 .
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:
// 의 머티리얼(Material).
UMaterial* Material;
// 의 머티리얼(Material)인스턴스 .
UMaterialInstance* MaterialInstance;
// 머티리얼(Material)의 , SetMaterialProperty호출(Call).
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 데이터보다 우선순위를 갖습니다. 예를 들면 다음과 같습니다.
// 페치/가져오기(Fetch)셰이딩 모델
FMaterialShadingModelField FMaterialResource::GetShadingModels() const
{
// 을(를) 활용하여 MaterialInstance의 데이터 .
return MaterialInstance ? MaterialInstance->GetShadingModels() : Material->GetShadingModels();
}FMaterialResource는 UMaterial 인스턴스가 유효한지, MaterialInstance가 비어 있을 수 있는지 확인해야 한다는 점에 유의해야 합니다.
FMaterialResource에는 ULandscapeMaterialInstanceConstant를 렌더링하는 재질에 해당하는 하위 클래스 FLandscapeMaterialResource도 있습니다.
FMaterial 외에도 렌더링 리소스의 핵심 개념은 FMaterialRenderContext이며, 이는 FMaterialRenderProxy와 FMaterial 간의 연결 쌍을 저장합니다.
struct ENGINE_API FMaterialRenderContext
{
// 을(를) 활용하여 머티리얼(Material)shader의 머티리얼(Material)렌더링(Render)테이블 .
const FMaterialRenderProxy* MaterialRenderProxy;
// 머티리얼(Material)렌더링(Render)리소스 .
const FMaterial& Material;
// 는 내에서 의 컬러 .
bool bShowSelection;
// 함수 .
FMaterialRenderContext(const FMaterialRenderProxy* InMaterialRenderProxy, const FMaterial& InMaterial, const FSceneView* InView);
};FMaterialRenderContext는 주로 다음과 같은 다양한 유형의 머티리얼 인터페이스의 형식 매개변수로 사용됩니다.
// FDefaultMaterialInstance 내에서 의 페치/가져오기(Fetch)벡터 파라미터 , 을(를) 활용하여 까지 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);
}
}9.2.5 재료 개요
이전 섹션에서는 머티리얼 시스템 내의 기본 유형, 개념 및 정의에 대해 자세히 설명했습니다. 이 섹션에서는 UML 다이어그램으로 직접 이동하여 관계를 개요합니다.
클래스 다이어그램-v2 UObject <|--UMaterialInterface UMaterialInterface <|-- UMaterial UMaterialInterface <|-- UMaterialInstance UMaterialInstance <|-- UMaterialInstanceConstant UMaterialInstanceConstant <|-- ULandscapeMaterialInstanceConstant UMaterialInstance <|-- UMaterialInstanceDynamic
FRenderResource <|-- FMaterialRenderProxy
FMaterialRenderProxy <|-- FDefaultMaterialInstance
FMaterialRenderProxy <|-- FMaterialInstanceResource
FMaterialRenderProxy <|-- FColoredMaterialRenderProxy
FMaterial <|-- FMaterialResource
FMaterialResource <|-- FLandscapeMaterialResource
FMaterialRenderProxy <-- FMaterialContext
FMaterial <-- FMaterialContext
UMaterial <-- UMaterialInterface
FMaterialRenderProxy <-- UMaterialInterface
클래스 UMaterialInterface{
UMaterial* GetBaseMaterial()
UMaterial* GetMaterial()
FMaterialRenderProxy* GetRenderProxy()
}
MaterialResources o--UMaterial
클래스 UMaterial{
TArray<FMaterialResource*> 머티리얼리소스
}
클래스 UMaterialInstance{
UMaterialInterface* 상위
}
FMaterial <-- FMaterialRenderProxy
FMaterialRenderProxy 클래스{
FMaterial* GetMaterial()
정적 TSet<FMaterialRenderProxy*> MaterialRenderProxyMap
}
클래스 FMaterialInstanceResource{
UMaterialInterface* 상위
UMaterialInterface* GameThreadParent
UMaterialInstance* 소유자
}
클래스 FDefaultMaterialInstance{
U재료* 재료
}
클래스 FMaterial{
FMaterialShader맵 게임ThreadShaderMap
FMaterialShader맵 렌더링ThreadShaderMap
}
머티리얼 <-- FMaterialResource
MaterialInstance <-- FMaterialResource
FMaterialResource 클래스{
U재료* 재료
UMaterialInstance* 머티리얼인스턴스
}
위 이미지의 텍스트가 너무 작아서 명확하게 볼 수 없는 경우 아래 이미지 버전을 클릭하여 확대할 수 있습니다.

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 및 다양한 셰이더 매개변수와 같은 머티리얼을 사용하는 메시의 다른 정보를 포함할 수 없음을 의미합니다.
그리하여 FMMaterial이 탄생하게 되었습니다. FMaterial은 동일한 유형의 엔진 모듈이지만 게임 스레드와 렌더링 스레드의 두 가지 ShaderMap을 저장합니다. 이는 렌더링 모듈이 게임 스레드의 액세스에 영향을 주지 않고 렌더링 스레드의 ShaderMap에 자유롭게 액세스할 수 있음을 의미합니다. 게다가 FMaterial에는 재질을 렌더링하는 데 필요한 모든 데이터가 포함되어 있습니다. 렌더러의 다른 위치에서는 그리드의 FMaterial을 얻는 한 일반적으로 재료 데이터를 얻고 도면 지침을 제출할 수 있습니다. 예를 들어 FBasePassMeshProcessor::AddMeshBatch의 코드는 다음과 같습니다.
// 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;
// 페치/가져오기(Fetch)FMaterial인스턴스 .
const FMaterial& Material = MeshBatch.MaterialRenderProxy->GetMaterialWithFallback(FeatureLevel, FallbackMaterialRenderProxyPtr);
const FMaterialRenderProxy& MaterialRenderProxy = FallbackMaterialRenderProxyPtr ? *FallbackMaterialRenderProxyPtr : *MeshBatch.MaterialRenderProxy;
// FMaterial인터페이스 페치/가져오기(Fetch)머티리얼(Material)데이터 .
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);
(......)
}9.3 재료 메커니즘
이 장에서는 주로 머티리얼 렌더링, 컴파일 메커니즘 및 프로세스, 머티리얼 캐싱 전략 등과 같은 머티리얼의 기본 메커니즘 중 일부를 분석합니다.
9.3.1 머티리얼 렌더링
이 섹션에서는 재료의 데이터 전송, 업데이트 및 렌더링 논리를 설명합니다.
머티리얼 데이터의 시작자는 여전히 게임 스레드 측의 리소스입니다. 이는 일반적으로 디스크에서 로드된 다음 UMaterialInterface 인스턴스로 직렬화되는 바이너리 리소스이거나 머티리얼 데이터가 런타임에 의해 동적으로 생성 및 설정됩니다. 그러나 대부분은 디스크에서 로드됩니다.
머티리얼을 사용하는 프리미티브 컴포넌트가 그리드 요소를 수집하도록 요청받은 경우, 사용하는 UMaterialInterface에 해당하는 FMaterialRenderProxy를 FMeshBatchElement에 전달할 수 있습니다. 다음은 StaticMesh를 예로 들어 설명합니다.
// 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];
// 페치/가져오기(Fetch)머티리얼(Material)의 인스턴스 (UMaterialInterface, FMaterialRenderProxy 및 FMaterial)
UMaterialInterface* MaterialInterface = ProxyLODInfo.Sections[SectionIndex].Material;
FMaterialRenderProxy* MaterialRenderProxy = MaterialInterface->GetRenderProxy();
const FMaterial* Material = MaterialRenderProxy->GetMaterial(FeatureLevel);
FMeshBatchElement& OutMeshBatchElement = OutMeshBatch.Elements[0];
// 처리(Process)포인트
const FVertexFactory* VertexFactory = nullptr;
if (ProxyLODInfo.OverrideColorVertexBuffer)
{
(......)
}
(......)
if(NumPrimitives > 0)
{
OutMeshBatch.SegmentIndex = SectionIndex;
OutMeshBatch.LODIndex = LODIndex;
// 머티리얼(Material) 및 렌더링(Render)테이블 .
OutMeshBatch.MaterialRenderProxy = MaterialRenderProxy;
(......)
}
}따라서 구성 요소가 메시 요소를 수집하면 재료에 대한 모든 유형의 데이터가 준비되고 액세스할 수 있음을 알 수 있습니다. 게임 스레드 단계에서는 다양한 유형의 머티리얼 인스턴스가 로드, 설정 및 생성되었습니다. 정확히 언제 만들어졌는지 계속해서 살펴보겠습니다. 먼저 FMaterialRenderProxy를 살펴보세요. 다음 코드에 표시된 것처럼 UMaterialInterface의 다양한 하위 클래스는 약간 다릅니다.
// Engine\Source\Runtime\Engine\Private\Materials\MaterialInstance.cpp
void UMaterialInstance::PostInitProperties()
{
Super::PostInitProperties();
if(!HasAnyFlags(RF_ClassDefaultObject))
{
// 생성(Create)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))
{
// 생성(Create)FMaterialRenderProxy.
DefaultMaterialInstance = new FDefaultMaterialInstance(this);
}
FPlatformMisc::CreateGuid(StateId);
}
FMaterialRenderProxy* UMaterial::GetRenderProxy() const
{
return DefaultMaterialInstance;
}UMaterialInstance에 해당하는 FMaterialRenderProxy가 서브클래스의 PostInitProperties 단계에서 생성된 것으로 유추할 수 있습니다.
우리는 UMaterialInterface에서 얻은 해당 FMaterial 인스턴스가 어떤 인터페이스와 멤버인지 계속해서 알아냅니다.
// Engine\Source\Runtime\Engine\Private\Materials\Material.cpp
// 페치/가져오기(Fetch)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를 찾고 있으므로 언제 생성되었는지 계속 알아봅시다.
FMaterialResource* FindOrCreateMaterialResource(TArray<FMaterialResource*>& MaterialResources,
UMaterial* OwnerMaterial,
UMaterialInstance* OwnerMaterialInstance,
ERHIFeatureLevel::Type InFeatureLevel,
EMaterialQualityLevel::Type InQualityLevel)
{
(......)
FMaterialResource* CurrentResource = FindMaterialResource(MaterialResources, InFeatureLevel, QualityLevelForResource, false);
// 만약 현재(Current)리소스 리스트 생성(Create)의 FMaterialResource인스턴스 .
if (!CurrentResource)
{
// 을(를) 활용하여 머티리얼(Material)인스턴스 의 의 인터페이스 생성(Create).
CurrentResource = OwnerMaterialInstance ? OwnerMaterialInstance->AllocatePermutationResource() : OwnerMaterial->AllocateResource();
CurrentResource->SetMaterial(OwnerMaterial, OwnerMaterialInstance, InFeatureLevel, QualityLevelForResource);
// 추가(Add)까지 FMaterialResource인스턴스 리스트 .
MaterialResources.Add(CurrentResource);
}
(......)
return CurrentResource;
}FMaterialResource 인스턴스를 생성할 때 유효한 OwnerMaterialInstance가 먼저 사용되고 UMaterial 인터페이스가 사용됩니다. 다음은 FMaterialResource 인스턴스를 생성하기 위한 인터페이스입니다.
FMaterialResource* UMaterialInstance::AllocatePermutationResource()
{
return new FMaterialResource();
}
FMaterialResource* UMaterial::AllocateResource()
{
return new FMaterialResource();
}좋은 여러분, 논리는 동일합니다. 새로운 FMaterialResource 객체를 직접 생성하고 반환하는 것입니다. FindOrCreateMaterialResource를 호출하는 인터페이스를 계속 추적해 보겠습니다.
ProcessSerializedInlineShaderMaps
UMaterial::PostLoad
UMaterial::CacheResourceShadersForRendering
UMaterial::AllMaterialsCacheResourceShadersForRendering
UMaterial::ForceRecompileForRendering
UMaterial::PostEditChangePropertyInternal
-UMerial::SetMaterialUsage
UMaterial::UpdateMaterialShaders
UMaterial::UpdateMaterialShaderCacheAndTextureReferences
위의 인터페이스는 FindOrCreateMaterialResource 인터페이스를 직접 또는 간접적으로 호출하여 FMaterialResource 객체 생성을 트리거합니다. 그러나 런타임 버전에서는 일반적으로 UMaterial::PostLoad에 의해 트리거되며 호출 스택은 다음과 같습니다.
- UMaterial::PostLoad
프로세스직렬화된인라인셰이더맵
재료자원 찾기또는만들기
또한 UMaterialInstance의 일부 인터페이스는 FMaterialResource 인스턴스 생성을 트리거하지만 이 문서에서는 더 이상 추적하지 않습니다.
우리는 FMaterial의 GameThreadShaderMap 및 RenderingThreadShaderMap이 설정되고 전달되는 위치와 시기를 계속 연구합니다.
// 설정(Set)RenderingThreadShaderMap
void FMaterial::SetRenderingThreadShaderMap(const TRefCountPtr<FMaterialShaderMap>& InMaterialShaderMap)
{
RenderingThreadShaderMap = InMaterialShaderMap;
}
// 설정(Set)스레드(Thread)ShaderMap.
void FMaterial::SetGameThreadShaderMap(FMaterialShaderMap* InMaterialShaderMap)
{
GameThreadShaderMap = InMaterialShaderMap;
TRefCountPtr<FMaterialShaderMap> ShaderMap = GameThreadShaderMap;
TRefCountPtr<FMaterial> Material = this;
// 렌더링(Render)스레드(Thread)설정(Set)ShaderMap의 .
ENQUEUE_RENDER_COMMAND(SetGameThreadShaderMap)([Material = MoveTemp(Material), ShaderMap = MoveTemp(ShaderMap)](FRHICommandListImmediate& RHICmdList) mutable
{
Material->RenderingThreadShaderMap = MoveTemp(ShaderMap);
});
}
// 설정(Set)ShaderMap
void FMaterial::SetInlineShaderMap(FMaterialShaderMap* InMaterialShaderMap)
{
GameThreadShaderMap = InMaterialShaderMap;
bContainsInlineShaders = true;
bLoadedCookedShaderMapId = true;
TRefCountPtr<FMaterialShaderMap> ShaderMap = GameThreadShaderMap;
TRefCountPtr<FMaterial> Material = this;
// 렌더링(Render)스레드(Thread)설정(Set)ShaderMap의 .
ENQUEUE_RENDER_COMMAND(SetInlineShaderMap)([Material = MoveTemp(Material), ShaderMap = MoveTemp(ShaderMap)](FRHICommandListImmediate& RHICmdList) mutable
{
Material->RenderingThreadShaderMap = MoveTemp(ShaderMap);
});
}위에서 설정할 수 있는 FMaterial의 RenderingThreadShaderMap에는 3개의 인터페이스가 있습니다. 어떤 인터페이스가 이를 호출하는지 계속 추적하세요.
- FMaterial::캐시셰이더
FMaterial::SetGameThreadShaderMap
- FMaterialShaderMap::LoadForRemoteRecompile
FMaterial::SetGameThreadShaderMap
- 프로세스직렬화된인라인셰이더맵
FMaterial::SetInlineShaderMap
- SetShaderMapsOnMaterialResources_RenderThread
FMaterial::SetRenderingThreadShaderMap
위의 인터페이스 중 다수는 결국 FMaterial의 RenderingThreadShaderMap으로 설정되지만 대부분의 경우 런타임에 RenderingThreadShaderMap이 설정되는 호출 스택은 다음과 같습니다.
- UMaterial::PostLoad
프로세스직렬화된인라인셰이더맵
FMaterial::SetInlineShaderMap
FMaterial의 RenderingThreadShaderMap이 올바르게 설정되면 재질과 관련된 다른 많은 데이터가 마치 푸른 바다에서 평온하게 헤엄치는 물고기처럼 렌더링 스레드와 렌더러에서 자유롭게 읽을 수 있습니다.
9.3.2 머티리얼 편집
이전 기사 Shader 시스템에서는 Shader의 컴파일 프로세스에 대해 설명했지만, 이 섹션에서는 머티리얼 블루프린트를 HLSL 코드로 변환하는 프로세스와 메커니즘에 대해 설명합니다.
이전 섹션에서는 먼저 머티리얼 청사진 편집 프로세스와 관련된 주요 유형과 개념을 이해했습니다.
9.3.2.1 UMaterialExpression
UMaterialExpression은 표현식입니다. 각 머티리얼 노드 UMaterialGraphNode에는 UMaterialExpression 인스턴스가 있습니다. 주요 정의는 다음과 같습니다.
// Engine\Source\Runtime\Engine\Classes\Materials\MaterialExpression.h
class ENGINE_API UMaterialExpression : public UObject
{
#if WITH_EDITORONLY_DATA
int32 MaterialExpressionEditorX;
int32 MaterialExpressionEditorY;
// 머티리얼(Material)포인트 .
UEdGraphNode* GraphNode;
#endif
// 의 머티리얼(Material).
class UMaterial* Material;
// 의 머티리얼(Material)함수 .
class UMaterialFunction* Function;
uint8 bIsParameterExpression : 1;
// 데이터 및 플래그 .
#if WITH_EDITORONLY_DATA
uint32 bShowInputs:1;
uint32 bShowOutputs:1;
TArray<FText> MenuCategories;
// 테이블 출력.
TArray<FExpressionOutput> Outputs;
#endif
//~ Begin UObject Interface.
virtual void PostInitProperties() override;
virtual void PostLoad() override;
virtual void PostDuplicate(bool bDuplicateForPIE) override;
virtual void Serialize( FStructuredArchive::FRecord Record ) override;
virtual bool IsEditorOnly() const
{
return true;
}
//~ End UObject Interface.
#if WITH_EDITOR
//
virtual int32 Compile(class FMaterialCompiler* Compiler, int32 OutputIndex) { return INDEX_NONE; }
virtual int32 CompilePreview(class FMaterialCompiler* Compiler, int32 OutputIndex) { return Compile(Compiler, OutputIndex); }
#endif
// 데이터 페치/가져오기(Fetch)인터페이스 .
virtual void GetTexturesForceMaterialRecompile(TArray<UTexture *> &Textures) const { }
virtual UObject* GetReferencedTexture() const { return nullptr; }
virtual bool CanReferenceTexture() const { return false; }
#if WITH_EDITOR
// 페치/가져오기(Fetch)입력테이블 .
bool GetAllInputExpressions(TArray<UMaterialExpression*>& InputExpressions);
// 파라미터 인터페이스 .
virtual bool HasAParameterName() const { return false; }
virtual void ValidateParameterName(const bool bAllowDuplicateName = true);
virtual bool HasClassAndNameCollision(UMaterialExpression* OtherExpression) const;
virtual void SetValueToMatchingExpression(UMaterialExpression* OtherExpression) {};
virtual FName GetParameterName() const { return NAME_None; }
virtual void SetParameterName(const FName& Name) {}
(......)
#endif // WITH_EDITOR
};UE에는 내장 머티리얼 노드가 많기 때문에 UMaterialExpression에서 상속된 서브클래스가 매우 많습니다(약 200개). 다음 그림은 서브클래스의 일부를 보여줍니다.

가장 일반적으로 사용되는 다음 두 가지 하위 범주를 분석 사례로 선택합니다.
// Engine\Source\Runtime\Engine\Classes\Materials\MaterialExpressionAdd.h
class UMaterialExpressionAdd : public UMaterialExpression
{
// 테이블 의 개 .
FExpressionInput A;
FExpressionInput B;
// A 및 B무효/유효하지 않음(Invalid)의 .
float ConstA;
float ConstB;
//~ Begin UMaterialExpression Interface
#if WITH_EDITOR
virtual int32 Compile(class FMaterialCompiler* Compiler, int32 OutputIndex) override;
virtual void GetCaption(TArray<FString>& OutCaptions) const override;
virtual FText GetKeywords() const override {return FText::FromString(TEXT("+"));}
#endif // WITH_EDITOR
//~ End UMaterialExpression Interface
};
// Engine\Source\Runtime\Engine\Private\Materials\MaterialExpressions.cpp
// 테이블 .
int32 UMaterialExpressionAdd::Compile(class FMaterialCompiler* Compiler, int32 OutputIndex)
{
// 페치/가져오기(Fetch)개 .
int32 Arg1 = A.GetTracedInput().Expression ? A.Compile(Compiler) : Compiler->Constant(ConstA);
int32 Arg2 = B.GetTracedInput().Expression ? B.Compile(Compiler) : Compiler->Constant(ConstB);
// 반환(Return)결과 .
return Compiler->Add(Arg1, Arg2);
}
// 페치/가져오기(Fetch).
void UMaterialExpressionAdd::GetCaption(TArray<FString>& OutCaptions) const
{
FString ret = TEXT("Add");
FExpressionInput ATraced = A.GetTracedInput();
FExpressionInput BTraced = B.GetTracedInput();
if(!ATraced.Expression || !BTraced.Expression)
{
ret += TEXT("(");
ret += ATraced.Expression ? TEXT(",") : FString::Printf( TEXT("%.4g,"), ConstA);
ret += BTraced.Expression ? TEXT(")") : FString::Printf( TEXT("%.4g)"), ConstB);
}
OutCaptions.Add(ret);
}
// Engine\Source\Runtime\Engine\Classes\Materials\MaterialExpressionDDX.h
class UMaterialExpressionDDX : public UMaterialExpression
{
FExpressionInput Value;
//~ Begin UMaterialExpression Interface
#if WITH_EDITOR
virtual int32 Compile(class FMaterialCompiler* Compiler, int32 OutputIndex) override;
virtual void GetCaption(TArray<FString>& OutCaptions) const override;
#endif
//~ End UMaterialExpression Interface
};
int32 UMaterialExpressionDDX::Compile(class FMaterialCompiler* Compiler, int32 OutputIndex)
{
int32 ValueInput = INDEX_NONE;
if(Value.GetTracedInput().Expression)
{
ValueInput = Value.Compile(Compiler);
}
if(ValueInput == INDEX_NONE)
{
return INDEX_NONE;
}
return Compiler->DDX(ValueInput);
}
void UMaterialExpressionDDX::GetCaption(TArray<FString>& OutCaptions) const
{
OutCaptions.Add(FString(TEXT("DDX")));
}위의 두 가지 유형은 컴파일 중에 컴파일러의 Add 및 DDX를 호출합니다. 다음으로 FMaterialCompiler(하위 클래스 FHLSLMaterialTranslator에 의해 구현되는 추상 클래스)의 두 인터페이스 구현을 입력합니다.
// 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));
}따라서 머티리얼 표현식의 컴파일은 실제로 매개변수와 해당 함수를 HLSL 조각으로 직렬화하는 것입니다.
9.3.2.2 UMaterialGraphNode
UMaterialGraphNode는 머티리얼 에디터에서 생성한 머티리얼 노드입니다. 상속받은 부모 클래스는 UMaterialGraphNode_Base, UEdGraphNode 순입니다. 그들의 정의는 다음과 같습니다:
// Engine\Source\Runtime\Engine\Classes\EdGraph\EdGraphNode.h
class ENGINE_API UEdGraphNode : public UObject
{
public:
//
TArray<UEdGraphPin*> Pins;
int32 NodePosX;
int32 NodePosY;
int32 NodeWidth;
int32 NodeHeight;
TEnumAsByte<ENodeAdvancedPins::Type> AdvancedPinDisplay;
private:
// 상태 및 플래그 .
ENodeEnabledState EnabledState;
ESaveOrphanPinMode OrphanedPinSaveMode;
uint8 bDisableOrphanPinSaving:1;
uint8 bDisplayAsDisabled:1;
uint8 bUserSetEnabledState:1;
uint8 bIsIntermediateNode : 1;
uint8 bHasCompilerMessage:1;
// 인터페이스 .
virtual bool IsInDevelopmentMode() const;
bool IsAutomaticallyPlacedGhostNode() const;
void MakeAutomaticallyPlacedGhostNode();
virtual void Serialize(FArchive& Ar) override;
(......)
};
// Engine\Source\Editor\UnrealEd\Classes\MaterialGraph\MaterialGraphNode_Base.h
class UMaterialGraphNode_Base : public UEdGraphNode
{
// Pin인터페이스 .
virtual void CreateInputPins() {};
virtual void CreateOutputPins() {};
virtual bool IsRootNode() const {return false;}
class UEdGraphPin* GetInputPin(int32 InputIndex) const;
UNREALED_API void GetInputPins(TArray<class UEdGraphPin*>& OutInputPins) const;
class UEdGraphPin* GetOutputPin(int32 OutputIndex) const;
UNREALED_API void GetOutputPins(TArray<class UEdGraphPin*>& OutOutputPins) const;
UNREALED_API void ReplaceNode(UMaterialGraphNode_Base* OldNode);
// 입력인터페이스 .
virtual int32 GetInputIndex(const UEdGraphPin* InputPin) const {return -1;}
virtual uint32 GetInputType(const UEdGraphPin* InputPin) const;
void InsertNewNode(UEdGraphPin* FromPin, UEdGraphPin* NewLinkPin, TSet<UEdGraphNode*>& OutNodeList);
//~ Begin UEdGraphNode Interface.
virtual void AllocateDefaultPins() override;
virtual void ReconstructNode() override;
virtual void RemovePinAt(const int32 PinIndex, const EEdGraphPinDirection PinDirection) override;
virtual void AutowireNewNode(UEdGraphPin* FromPin) override;
virtual bool CanCreateUnderSpecifiedSchema(const UEdGraphSchema* Schema) const override;
virtual FString GetDocumentationLink() const override;
//~ End UEdGraphNode Interface.
protected:
void ModifyAndCopyPersistentPinData(UEdGraphPin& TargetPin, const UEdGraphPin& SourcePin) const;
};
// Engine\Source\Editor\UnrealEd\Classes\MaterialGraph\MaterialGraphNode.h
class UMaterialGraphNode : public UMaterialGraphNode_Base
{
// 머티리얼(Material)테이블 .
class UMaterialExpression* MaterialExpression;
bool bPreviewNeedsUpdate;
bool bIsErrorExpression;
bool bIsPreviewExpression;
FRealtimeStateGetter RealtimeDelegate;
FSetMaterialDirty MaterialDirtyDelegate;
FSimpleDelegate InvalidatePreviewMaterialDelegate;
public:
UNREALED_API void PostCopyNode();
UNREALED_API FMaterialRenderProxy* GetExpressionPreview();
UNREALED_API void RecreateAndLinkNode();
UNREALED_API int32 GetOutputIndex(const UEdGraphPin* OutputPin);
uint32 GetOutputType(const UEdGraphPin* OutputPin);
//~ Begin UObject Interface
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
virtual void PostEditImport() override;
virtual void PostDuplicate(bool bDuplicateForPIE) override;
//~ End UObject Interface
//~ Begin UMaterialGraphNode_Base Interface
virtual void CreateInputPins() override;
virtual void CreateOutputPins() override;
virtual UNREALED_API int32 GetInputIndex(const UEdGraphPin* InputPin) const override;
virtual uint32 GetInputType(const UEdGraphPin* InputPin) const override;
//~ End UMaterialGraphNode_Base Interface
(......)
};머티리얼 노드에는 그래픽 인터페이스의 정보와 해당 표현식이 포함되어 있으며 뷰와 데이터를 분리하는 고전적인 디자인 패턴을 채택합니다.
9.3.2.3 UMaterialGraph
UMaterialGraph는 UMaterial의 멤버로, 편집기에서 생성된 머티리얼 노드와 매개변수를 저장하는 데 사용됩니다. 해당 유형과 관련 유형은 다음과 같이 정의됩니다.
// Engine\Source\Runtime\Engine\Classes\EdGraph\EdGraph.h
class ENGINE_API UEdGraph : public UObject
{
public:
// .
TSubclassOf<class UEdGraphSchema> Schema;
// 포인트 .
TArray<class UEdGraphNode*> Nodes;
uint32 bEditable:1;
uint32 bAllowDeletion:1;
uint32 bAllowRenaming:1;
(......)
public:
FDelegateHandle AddOnGraphChangedHandler( const FOnGraphChanged::FDelegate& InHandler );
void RemoveOnGraphChangedHandler( FDelegateHandle Handle );
//~ Begin UObject interface
virtual void BuildSubobjectMapping(UObject* OtherObject, TMap<UObject*, UObject*>& ObjectMapping) const override;
// 포인트 .
template <typename NodeClass>
NodeClass* CreateIntermediateNode();
void AddNode( UEdGraphNode* NodeToAdd, bool bUserAction = false, bool bSelectNewNode = true );
bool RemoveNode( UEdGraphNode* NodeToRemove, bool bBreakAllLinks = true );
(......)
protected:
// 생성(Create)포인트 .
UEdGraphNode* CreateNode( TSubclassOf<UEdGraphNode> NewNodeClass, bool bFromUI, bool bSelectNewNode );
UEdGraphNode* CreateNode(TSubclassOf<UEdGraphNode> NewNodeClass, bool bSelectNewNode = true)
UEdGraphNode* CreateUserInvokedNode(TSubclassOf<UEdGraphNode> NewNodeClass, bool bSelectNewNode = true)
private:
FOnGraphChanged OnGraphChanged;
};
// Engine\Source\Editor\UnrealEd\Classes\MaterialGraph\MaterialGraph.h
class UNREALED_API UMaterialGraph : public UEdGraph
{
// 의 머티리얼(Material)인스턴스 .
class UMaterial* Material;
// 머티리얼(Material)함수 .
class UMaterialFunction* MaterialFunction;
// 포인트 .
class UMaterialGraphNode_Root* RootNode;
// 머티리얼(Material)입력리스트 .
TArray<FMaterialInputInfo> MaterialInputs;
// .
FRealtimeStateGetter RealtimeDelegate;
FSetMaterialDirty MaterialDirtyDelegate;
FToggleExpressionCollapsed ToggleCollapsedDelegate;
public:
// 머티리얼(Material).
void RebuildGraph();
// 추가(Add)테이블 까지 머티리얼(Material).
class UMaterialGraphNode* AddExpression(UMaterialExpression* Expression, bool bUserInvoked);
class UMaterialGraphNode_Comment* AddComment(UMaterialExpressionComment* Comment, bool bIsUserInvoked = false);
// 포인트 .
void LinkGraphNodesFromMaterial();
void LinkMaterialExpressionsFromGraph() const;
(......)
};9.3.2.4 FHLSLMaterialTranslator
FHLSLMaterialTranslator는 FMaterialCompiler에서 상속됩니다. 그 기능은 머티리얼 표현식을 HLSL 코드로 변환하고 MaterialTemplate.ush의 매크로와 빈 코드 세그먼트를 채우는 것입니다. 이는 다음과 같이 정의됩니다.
// Engine\Source\Runtime\Engine\Public\MaterialCompiler.h
class FMaterialCompiler
{
public:
virtual ~FMaterialCompiler() { }
// 머티리얼(Material)인터페이스 .
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;
// 호출(Call)머티리얼(Material)테이블 .
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;
(......)
// 머티리얼(Material)테이블 의 인터페이스 .
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:
// 의 머티리얼(Material).
FMaterial* Material;
// 출력결과 , 저장(Store)까지 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<FGuid> MaterialAttributesStack;
TArray<FMaterialParameterInfo> ParameterOwnerStack;
TArray<FShaderCodeChunk>* CurrentScopeChunks;
bool SharedPixelProperties[CompiledMP_MAX];
TArray<FMaterialFunctionCompileState*> FunctionStacks[SF_NumFrequencies];
FStaticParameterSet StaticParameters;
TArray<FShaderCodeChunk> SharedPropertyCodeChunks[SF_NumFrequencies];
TArray<FShaderCodeChunk> UniformExpressions;
TArray<TRefCountPtr<FMaterialUniformExpression> > UniformVectorExpressions;
TArray<TRefCountPtr<FMaterialUniformExpression> > UniformScalarExpressions;
TArray<TRefCountPtr<FMaterialUniformExpressionTexture> > UniformTextureExpressions[NumMaterialTextureParameterTypes];
TArray<TRefCountPtr<FMaterialUniformExpressionExternalTexture>> UniformExternalTextureExpressions;
TArray<UMaterialParameterCollection*> ParameterCollections;
TArray<FMaterialCustomExpressionEntry> CustomExpressions;
TArray<FString> CustomOutputImplementations;
TArray<UMaterialExpressionVertexInterpolator*> CustomVertexInterpolators;
// 포인트 .
TArray<FMaterialVTStackEntry> VTStacks;
FHashTable VTStackHash;
TBitArray<> AllocatedUserTexCoords;
TBitArray<> AllocatedUserVertexTexCoords;
(.....)
public:
// 실행(Execute)HLSL.
bool Translate();
// 페치/가져오기(Fetch)머티리얼(Material).
void GetMaterialEnvironment(EShaderPlatform InPlatform, FShaderCompilerEnvironment& OutEnvironment);
void GetSharedInputsMaterialCode(FString& PixelMembersDeclaration, FString& NormalAssignment, FString& PixelMembersInitializationEpilog);
// 페치/가져오기(Fetch)머티리얼(Material)셰이딩 코드 .
FString GetMaterialShaderCode();
protected:
// 페치/가져오기(Fetch)정의 .
FString GetDefinitions(TArray<FShaderCodeChunk>& 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, ...);
// 머티리얼(Material)테이블 .
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* 재질: 컴파일된 대상 재질입니다.
FMaterialCompilationOutput& MaterialCompilationOutput: 컴파일된 결과.
FString MaterialTemplate: 채워지거나 채워질 MaterialTemplate.ush 문자열입니다.
Translate(): HLSL 번역을 실행하고 표현식을 코드 블록으로 번역한 후 해당 속성 슬롯에 저장합니다.
GetMaterialShaderCode(): 재질의 매크로, 속성, 표현식 및 기타 데이터를 MaterialTemplate.ush에 채우고 결과를 반환합니다.
FHLSLMaterialTranslator의 번역 프로세스를 구체적으로 설명하는 섹션이 나중에 있습니다.
게다가 FMaterialCompiler에는 라이트매스 렌더러와 머티리얼 베이킹에 사용되는 서브클래스 FProxyMaterialCompiler도 있습니다.
9.3.2.5 MaterialTemplate.ush
MaterialTemplate.usf는 대체할 많은 수의 %s 공석과 매크로를 포함하는 재질 셰이더 템플릿입니다. FHLSLMaterialTranslator::GetMaterialShaderCode로 채워집니다. 원본 코드의 일부는 다음과 같습니다.
// 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
// 포인트 보간(Interpolation)위치 정의 .
%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)
(......)
// 머티리얼(Material)의 구조체 .
struct FMaterialAttributes
{
%s
};
struct FPixelMaterialInputs
{
%s
};
// 픽셀(Pixel)파라미터 .
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);
(......)
// 머티리얼(Material)테이블 인터페이스 .
float MaterialExpressionDepthOfFieldFunction(float SceneDepth, int FunctionValueIndex);
float2 MaterialExpressionGetAtlasUVs(FMaterialPixelParameters Parameters);
float4 MaterialExpressionGetHairAuxilaryData(FMaterialPixelParameters Parameters);
float3 MaterialExpressionGetHairColorFromMelanin(float Melanin, float Redness, float3 DyeColor);
(......)
// 머티리얼(Material)탐색/조회(Find/Lookup).
MaterialFloat4 ProcessMaterialColorTextureLookup(MaterialFloat4 TextureValue);
MaterialFloat4 ProcessMaterialVirtualColorTextureLookup(MaterialFloat4 TextureValue);
MaterialFloat4 ProcessMaterialExternalTextureLookup(MaterialFloat4 TextureValue);
MaterialFloat4 ProcessMaterialLinearColorTextureLookup(MaterialFloat4 TextureValue);
MaterialFloat ProcessMaterialGreyscaleTextureLookup(MaterialFloat TextureValue);
(......)
// 머티리얼(Material)테이블 .
%s
// 머티리얼(Material)페치/가져오기(Fetch)인터페이스 .
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);
(......)
// 계산/산출(Calculate)머티리얼(Material)파라미터 인터페이스 .
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에는 주로 다음 범주에 속하는 많은 양의 데이터와 인터페이스가 포함되어 있습니다.
기본 셰이더 모듈 참조.
매크로 정의를 입력해야 합니다.
채워질 인터페이스 구현.
꼭지점, 픽셀, 재질 속성 등의 구조를 정의합니다. 구조의 일부를 채워야 합니다.
재료 속성, 데이터 처리, 표현 및 도구 인터페이스 정의. 일부 인터페이스를 채워야 합니다.
9.3.2.6 머티리얼 컴파일 프로세스
위 섹션에서는 자료 편집과 관련된 핵심 유형을 자세히 분석했습니다. 이 섹션에서는 머티리얼 블루프린트를 HLSL 코드로 컴파일하는 프로세스를 직접 분석합니다.
재질 ShaderMap의 컴파일 항목은 FMaterial의 다음 두 인터페이스에 있습니다.
FMaterial::BeginCompileShaderMap
FMaterial::GetMaterialExpressionSource
그러나 BeginCompileShaderMap은 기본 프로세스에 있으며 적용 가능성이 더 높습니다. 이를 재료 청사진의 편집 프로세스를 분석하기 위한 출발점으로 사용해 보겠습니다.
// Engine\Source\Runtime\Engine\Private\Materials\MaterialShared.cpp
bool FMaterial::BeginCompileShaderMap(const FMaterialShaderMapId& ShaderMapId, const FStaticParameterSet &StaticParameterSet,
EShaderPlatform Platform, TRefCountPtr<FMaterialShaderMap>& OutShaderMap, const ITargetPlatform* TargetPlatform)
{
// ※ 주의: 오직 실행(Execute).
#if WITH_EDITORONLY_DATA
bool bSuccess = false;
// shader map.
TRefCountPtr<FMaterialShaderMap> NewShaderMap = new FMaterialShaderMap();
#if WITH_EDITOR
NewShaderMap->AssociateWithAsset(GetAssetPath());
#endif
// 머티리얼(Material)shader코드 .
// 출력결과 .
FMaterialCompilationOutput NewCompilationOutput;
// 변환(Transform/Convert).
FHLSLMaterialTranslator MaterialTranslator(this, NewCompilationOutput, StaticParameterSet, Platform,GetQualityLevel(), ShaderMapId.FeatureLevel, TargetPlatform);
// 실행(Execute)테이블 변환(Transform/Convert), 패딩/채우기 까지 MaterialTemplate.ush.
bSuccess = MaterialTranslator.Translate();
// 테이블 변환(Transform/Convert)실행(Execute).
if(bSuccess)
{
// 로 머티리얼(Material)생성(Create)개 셰이딩 ,의 머티리얼(Material).
TRefCountPtr<FShaderCompilerEnvironment> MaterialEnvironment = new FShaderCompilerEnvironment();
MaterialEnvironment->TargetPlatform = TargetPlatform;
// 페치/가져오기(Fetch)머티리얼(Material).
MaterialTranslator.GetMaterialEnvironment(Platform, *MaterialEnvironment);
// 페치/가져오기(Fetch)머티리얼(Material)shader코드 .
const FString MaterialShaderCode = MaterialTranslator.GetMaterialShaderCode();
const bool bSynchronousCompile = RequiresSynchronousCompilation() || !GShaderCompilingManager->AllowAsynchronousShaderCompiling();
// 의 머티리얼(Material).
MaterialEnvironment->IncludeVirtualPathToContentsMap.Add(TEXT("/Engine/Generated/Material.ush"), MaterialShaderCode);
// 머티리얼(Material)의 shader코드 .
NewShaderMap->Compile(this, ShaderMapId, MaterialEnvironment, NewCompilationOutput, Platform, bSynchronousCompile);
if (bSynchronousCompile) // 동기화
{
// 동기화 , OutShaderMap.
OutShaderMap = NewShaderMap->CompiledSuccessfully() ? NewShaderMap : nullptr;
}
else // 비동기
{
// NewShaderMap까지 의 리스트 .
OutstandingCompileShaderMapIds.AddUnique( NewShaderMap->GetCompilingId() );
// 비동기 , OutShaderMap로 null, 까지 기본값(Default)의 머티리얼(Material).
OutShaderMap = nullptr;
}
}
return bSuccess;
#else
UE_LOG(LogMaterial, Fatal,TEXT("Not supported."));
return false;
#endif
}위의 인터페이스는 데이터 초기화, 새 컴파일 객체 생성, 머티리얼 청사진 변환 수행 등 여러 단계로 구분됩니다. 변환이 성공하면 머티리얼 셰이더 컴파일 환경을 만든 다음 변환된 머티리얼 블루프린트 셰이더 코드를 컴파일합니다. 마지막으로 비동기적으로 처리되는지 여부에 따라 ShaderMap을 반환합니다. 동기식인 경우 직접 반환됩니다. 비동기식인 경우 먼저 컴파일 완료를 기다리는 OutstandingCompileShaderMapId 목록에 배치됩니다.
OutstandingCompileShaderMapIds를 처리하기 위한 논리적 스택 체인은 다음과 같습니다.
- UMaterial::직렬화
직렬화인라인셰이더 맵
FMaterial::SerializeInlineShaderMap
FMaterial::Finish컴파일
FMaterial::GetShaderMapIDsWithUnfinishedCompilation
FMaterial::GetShaderMapIDsWithUnfinishedCompilation의 코드는 다음과 같습니다.
void FMaterial::GetShaderMapIDsWithUnfinishedCompilation(TArray<int32>& ShaderMapIds)
{
// 추가(Add)의 ShaderMapId까지 리스트 .(검사/감지(Detect)GameThreadShaderMap, 검사/감지(Detect)OutstandingCompileShaderMapIds)
if (GameThreadShaderMap && !GameThreadShaderMap->IsCompilationFinalized())
{
ShaderMapIds.Add(GameThreadShaderMap->GetCompilingId());
}
else if (OutstandingCompileShaderMapIds.Num() != 0 )
{
ShaderMapIds.Append(OutstandingCompileShaderMapIds);
}
}FMaterial::BeginCompileShaderMap에는 해결되지 않은 FHLSLMaterialTranslator의 몇 가지 중요한 인터페이스도 있습니다. 모두 다음과 같습니다.
// Engine\Source\Runtime\Engine\Private\Materials\HLSLMaterialTranslator.cpp
bool FHLSLMaterialTranslator::Translate()
{
bSuccess = true;
// 출력저장(Save)까지 MaterialCompilationOutput, 로써 저장(Save)까지 DDC.
Material->CompileErrors.Empty();
Material->ErrorExpressions.Empty();
bCompileForComputeShader = Material->IsLightFunction();
int32 NormalCodeChunkEnd = -1;
int32 Chunk[CompiledMP_MAX];
memset(Chunk, INDEX_NONE, sizeof(Chunk));
// 정의 포인트 보간(Interpolation),로써 타입/유형 정보 을(를) 활용하여 .
{
CustomVertexInterpolators.Empty();
CurrentCustomVertexInterpolatorOffset = 0;
NextVertexInterpolatorIndex = 0;
MaterialProperty = MP_MAX;
ShaderFrequency = SF_Vertex;
TArray<UMaterialExpression*> Expressions;
Material->GatherExpressionsForCustomInterpolators(Expressions);
GatherCustomVertexInterpolators(Expressions);
// 리셋/초기화(Reset)의 힙 데이터 .
while (FunctionStacks[SF_Vertex].Num() > 1)
{
FMaterialFunctionCompileState* Stack = FunctionStacks[SF_Vertex].Pop(false);
delete Stack;
}
FunctionStacks[SF_Vertex][0]->Reset();
// 테이블 리스트 을(를) 활용하여 ,적용 포인트 .
int32 NumMaterialLayersAttributes = 0;
for (UMaterialExpression* Expression : Expressions)
{
if (UMaterialExpressionMaterialAttributeLayers* Layers = Cast<UMaterialExpressionMaterialAttributeLayers>(Expression))
{
++NumMaterialLayersAttributes;
if (NumMaterialLayersAttributes > 1)
{
Errorf(TEXT("Materials can contain only one Material Attribute Layers node."));
break;
}
}
}
}
const EShaderFrequency NormalShaderFrequency = FMaterialAttributeDefinitionMap::GetShaderFrequency(MP_Normal);
const EMaterialDomain Domain = Material->GetMaterialDomain();
const EBlendMode BlendMode = Material->GetBlendMode();
// 정의 출력테이블 의 구현 .
TArray<UMaterialExpressionCustomOutput*> CustomOutputExpressions;
Material->GatherCustomOutputExpressions(CustomOutputExpressions);
TSet<UClass*> SeenCustomOutputExpressionsClasses;
// 정의 출력필수: ,로써 로써 로써 입력을(를) 활용하여 .
CompileCustomOutputs(CustomOutputExpressions, SeenCustomOutputExpressionsClasses, true);
// 노멀(Normal).
{
Chunk[MP_Normal] = Material->CompilePropertyAndSetMaterialProperty(MP_Normal, this);
NormalCodeChunkEnd = SharedPropertyCodeChunks[NormalShaderFrequency].Num();
}
(......)
// 머티리얼(Material).
Chunk[MP_EmissiveColor] = Material->CompilePropertyAndSetMaterialProperty(MP_EmissiveColor ,this);
Chunk[MP_DiffuseColor] = Material->CompilePropertyAndSetMaterialProperty(MP_DiffuseColor ,this);
Chunk[MP_SpecularColor] = Material->CompilePropertyAndSetMaterialProperty(MP_SpecularColor ,this);
Chunk[MP_BaseColor] = Material->CompilePropertyAndSetMaterialProperty(MP_BaseColor ,this);
Chunk[MP_Metallic] = Material->CompilePropertyAndSetMaterialProperty(MP_Metallic ,this);
Chunk[MP_Specular] = Material->CompilePropertyAndSetMaterialProperty(MP_Specular ,this);
Chunk[MP_Roughness] = Material->CompilePropertyAndSetMaterialProperty(MP_Roughness ,this);
Chunk[MP_Anisotropy] = Material->CompilePropertyAndSetMaterialProperty(MP_Anisotropy ,this);
Chunk[MP_Opacity] = Material->CompilePropertyAndSetMaterialProperty(MP_Opacity ,this);
Chunk[MP_OpacityMask] = Material->CompilePropertyAndSetMaterialProperty(MP_OpacityMask ,this);
Chunk[MP_Tangent] = Material->CompilePropertyAndSetMaterialProperty(MP_Tangent ,this);
Chunk[MP_WorldPositionOffset] = Material->CompilePropertyAndSetMaterialProperty(MP_WorldPositionOffset ,this);
Chunk[MP_WorldDisplacement] = Material->CompilePropertyAndSetMaterialProperty(MP_WorldDisplacement ,this);
Chunk[MP_TessellationMultiplier] = Material->CompilePropertyAndSetMaterialProperty(MP_TessellationMultiplier ,this);
// 처리(Process)shading model.
Chunk[MP_ShadingModel] = Material->CompilePropertyAndSetMaterialProperty(MP_ShadingModel ,this);
FMaterialShadingModelField MaterialShadingModels = Material->GetShadingModels();
if (Material->IsShadingModelFromMaterialExpression() && ShadingModelsFromCompilation.IsValid())
{
MaterialShadingModels = ShadingModelsFromCompilation;
}
ValidateShadingModelsForFeatureLevel(MaterialShadingModels);
(......)
// 데이터 .
Chunk[MP_CustomData0] = Material->CompilePropertyAndSetMaterialProperty(MP_CustomData0 ,this);
Chunk[MP_CustomData1] = Material->CompilePropertyAndSetMaterialProperty(MP_CustomData1 ,this);
Chunk[MP_AmbientOcclusion] = Material->CompilePropertyAndSetMaterialProperty(MP_AmbientOcclusion ,this);
if (IsTranslucentBlendMode(BlendMode) || MaterialShadingModels.HasShadingModel(MSM_SingleLayerWater))
{
int32 UserRefraction = ForceCast(Material->CompilePropertyAndSetMaterialProperty(MP_Refraction, this), MCT_Float1);
int32 RefractionDepthBias = ForceCast(ScalarParameter(FName(TEXT("RefractionDepthBias")), Material->GetRefractionDepthBiasValue()), MCT_Float1);
Chunk[MP_Refraction] = AppendVector(UserRefraction, RefractionDepthBias);
}
(......)
ResourcesString = TEXT("");
(......)
// 코드 .
bAllowCodeChunkGeneration = false;
// 처리(Process) 및 머티리얼(Material)의 플래그 .
bUsesEmissiveColor = IsMaterialPropertyUsed(MP_EmissiveColor, Chunk[MP_EmissiveColor], FLinearColor(0, 0, 0, 0), 3);
bUsesPixelDepthOffset = (AllowPixelDepthOffset(Platform) && IsMaterialPropertyUsed(MP_PixelDepthOffset, Chunk[MP_PixelDepthOffset], FLinearColor(0, 0, 0, 0), 1))
|| (Domain == MD_DeferredDecal && Material->GetDecalBlendMode() == DBM_Volumetric_DistanceFunction);
bool bUsesWorldPositionOffsetCurrent = IsMaterialPropertyUsed(MP_WorldPositionOffset, Chunk[MP_WorldPositionOffset], FLinearColor(0, 0, 0, 0), 3);
bool bUsesWorldPositionOffsetPrevious = IsMaterialPropertyUsed(MP_WorldPositionOffset, Chunk[CompiledMP_PrevWorldPositionOffset], FLinearColor(0, 0, 0, 0), 3);
bUsesWorldPositionOffset = bUsesWorldPositionOffsetCurrent || bUsesWorldPositionOffsetPrevious;
MaterialCompilationOutput.bModifiesMeshPosition = bUsesPixelDepthOffset || bUsesWorldPositionOffset;
MaterialCompilationOutput.bUsesWorldPositionOffset = bUsesWorldPositionOffset;
MaterialCompilationOutput.bUsesPixelDepthOffset = bUsesPixelDepthOffset;
bIsFullyRough = Chunk[MP_Roughness] != INDEX_NONE && IsMaterialPropertyUsed(MP_Roughness, Chunk[MP_Roughness], FLinearColor(1, 0, 0, 0), 1) == false;
bUsesAnisotropy = IsMaterialPropertyUsed(MP_Anisotropy, Chunk[MP_Anisotropy], FLinearColor(0, 0, 0, 0), 1);
MaterialCompilationOutput.bUsesAnisotropy = bUsesAnisotropy;
if (bUsesSceneDepth)
{
MaterialCompilationOutput.SetIsSceneTextureUsed(PPI_SceneDepth);
}
MaterialCompilationOutput.bUsesDistanceCullFade = bUsesDistanceCullFade;
(......)
bool bDBufferAllowed = IsUsingDBuffers(Platform);
bool bDBufferBlendMode = IsDBufferDecalBlendMode((EDecalBlendMode)Material->GetDecalBlendMode());
FString InterpolatorsOffsetsDefinitionCode;
TBitArray<> FinalAllocatedCoords = GetVertexInterpolatorsOffsets(InterpolatorsOffsetsDefinitionCode);
MaterialCompilationOutput.NumUsedUVScalars = GetNumUserTexCoords() * 2;
MaterialCompilationOutput.NumUsedCustomInterpolatorScalars = CurrentCustomVertexInterpolatorOffset;
// 처리(Process)노멀(Normal)코드 .
{
GetFixedParameterCode(
0,
NormalCodeChunkEnd,
Chunk[MP_Normal],
SharedPropertyCodeChunks[NormalShaderFrequency],
TranslatedCodeChunkDefinitions[MP_Normal],
TranslatedCodeChunks[MP_Normal]);
if (TranslatedCodeChunkDefinitions[MP_Normal].IsEmpty())
{
TranslatedCodeChunkDefinitions[MP_Normal] = GetDefinitions(SharedPropertyCodeChunks[NormalShaderFrequency], 0, NormalCodeChunkEnd);
}
}
// 의 코드 , 무시/스킵(Ignore)노멀(Normal).
for(uint32 PropertyId = 0; PropertyId < MP_MAX; ++PropertyId)
{
if (PropertyId == MP_MaterialAttributes || PropertyId == MP_Normal || PropertyId == MP_CustomOutput)
{
continue;
}
const EShaderFrequency PropertyShaderFrequency = FMaterialAttributeDefinitionMap::GetShaderFrequency((EMaterialProperty)PropertyId);
int32 StartChunk = 0;
if (PropertyShaderFrequency == NormalShaderFrequency && SharedPixelProperties[PropertyId])
{
StartChunk = NormalCodeChunkEnd;
}
GetFixedParameterCode(
StartChunk,
SharedPropertyCodeChunks[PropertyShaderFrequency].Num(),
Chunk[PropertyId],
SharedPropertyCodeChunks[PropertyShaderFrequency],
TranslatedCodeChunkDefinitions[PropertyId],
TranslatedCodeChunks[PropertyId]);
}
// 처리(Process)머티리얼(Material).
for(uint32 PropertyId = MP_MAX; PropertyId < CompiledMP_MAX; ++PropertyId)
{
switch(PropertyId)
{
case CompiledMP_EmissiveColorCS:
if (bCompileForComputeShader)
{
GetFixedParameterCode(Chunk[PropertyId], SharedPropertyCodeChunks[SF_Compute], TranslatedCodeChunkDefinitions[PropertyId], TranslatedCodeChunks[PropertyId]);
}
break;
case CompiledMP_PrevWorldPositionOffset:
{
GetFixedParameterCode(Chunk[PropertyId], SharedPropertyCodeChunks[SF_Vertex], TranslatedCodeChunkDefinitions[PropertyId], TranslatedCodeChunks[PropertyId]);
}
break;
default: check(0);
break;
}
}
// 출력정의 출력테이블 의 구현 .
for (int32 ExpressionIndex = 0; ExpressionIndex < CustomOutputImplementations.Num(); ExpressionIndex++)
{
ResourcesString += CustomOutputImplementations[ExpressionIndex] + "\r\n\r\n";
}
// Uniform스칼라 테이블 .
for (const FMaterialUniformExpression* ScalarExpression : UniformScalarExpressions)
{
FMaterialUniformPreshaderHeader& Preshader = MaterialCompilationOutput.UniformExpressionSet.UniformScalarPreshaders.AddDefaulted_GetRef();
Preshader.OpcodeOffset = MaterialCompilationOutput.UniformExpressionSet.UniformPreshaderData.Num();
ScalarExpression->WriteNumberOpcodes(MaterialCompilationOutput.UniformExpressionSet.UniformPreshaderData);
Preshader.OpcodeSize = MaterialCompilationOutput.UniformExpressionSet.UniformPreshaderData.Num() - Preshader.OpcodeOffset;
}
// Uniform벡터 테이블 .
for (FMaterialUniformExpression* VectorExpression : UniformVectorExpressions)
{
FMaterialUniformPreshaderHeader& Preshader = MaterialCompilationOutput.UniformExpressionSet.UniformVectorPreshaders.AddDefaulted_GetRef();
Preshader.OpcodeOffset = MaterialCompilationOutput.UniformExpressionSet.UniformPreshaderData.Num();
VectorExpression->WriteNumberOpcodes(MaterialCompilationOutput.UniformExpressionSet.UniformPreshaderData);
Preshader.OpcodeSize = MaterialCompilationOutput.UniformExpressionSet.UniformPreshaderData.Num() - Preshader.OpcodeOffset;
}
// Uniform스칼라 테이블 .
for (uint32 TypeIndex = 0u; TypeIndex < NumMaterialTextureParameterTypes; ++TypeIndex)
{
MaterialCompilationOutput.UniformExpressionSet.UniformTextureParameters[TypeIndex].Empty(UniformTextureExpressions[TypeIndex].Num());
for (FMaterialUniformExpressionTexture* TextureExpression : UniformTextureExpressions[TypeIndex])
{
TextureExpression->GetTextureParameterInfo(MaterialCompilationOutput.UniformExpressionSet.UniformTextureParameters[TypeIndex].AddDefaulted_GetRef());
}
}
// 텍스처(Texture).
MaterialCompilationOutput.UniformExpressionSet.UniformExternalTextureParameters.Empty(UniformExternalTextureExpressions.Num());
for (FMaterialUniformExpressionExternalTexture* TextureExpression : UniformExternalTextureExpressions)
{
TextureExpression->GetExternalTextureParameterInfo(MaterialCompilationOutput.UniformExpressionSet.UniformExternalTextureParameters.AddDefaulted_GetRef());
}
// 로드 MaterialTemplate.ush의 코드 .
LoadShaderSourceFileChecked(TEXT("/Engine/Private/MaterialTemplate.ush"), GetShaderPlatform(), MaterialTemplate);
// 까지 '#line'의 위치 .
const int32 LineIndex = MaterialTemplate.Find(TEXT("#line"), ESearchCase::CaseSensitive);
// 계산/산출(Calculate)'#line'의 위치 .
MaterialTemplateLineNumber = INDEX_NONE;
int32 StartPosition = LineIndex + 1;
do
{
MaterialTemplateLineNumber++;
StartPosition = MaterialTemplate.Find(TEXT("\n"), ESearchCase::CaseSensitive, ESearchDir::FromEnd, StartPosition - 1);
}
while (StartPosition != INDEX_NONE);
MaterialTemplateLineNumber += 3;
// 머티리얼(Material)파라미터 .
MaterialCompilationOutput.UniformExpressionSet.SetParameterCollections(ParameterCollections);
// 생성(Create)머티리얼(Material)버퍼 구조체 .
MaterialCompilationOutput.UniformExpressionSet.CreateBufferStruct();
// 저장(Store)VT샘플링의 개수 .
MaterialCompilationOutput.EstimatedNumVirtualTextureLookups = NumVtSamples;
// 함수 힙 .
ClearAllFunctionStacks();
return bSuccess;
}
// 페치/가져오기(Fetch)머티리얼(Material)의 (정의 ).
void FHLSLMaterialTranslator::GetMaterialEnvironment(EShaderPlatform InPlatform, FShaderCompilerEnvironment& OutEnvironment)
{
if (bNeedsParticlePosition || Material->ShouldGenerateSphericalParticleNormals() || bUsesSphericalParticleOpacity)
{
OutEnvironment.SetDefine(TEXT("NEEDS_PARTICLE_POSITION"), 1);
}
if (bNeedsParticleVelocity || Material->IsUsedWithNiagaraMeshParticles())
{
OutEnvironment.SetDefine(TEXT("NEEDS_PARTICLE_VELOCITY"), 1);
}
(......)
OutEnvironment.SetDefine(TEXT("MATERIAL_ENABLE_TRANSLUCENCY_FOGGING"), Material->ShouldApplyFogging());
OutEnvironment.SetDefine(TEXT("MATERIAL_ENABLE_TRANSLUCENCY_CLOUD_FOGGING"), Material->ShouldApplyCloudFogging());
OutEnvironment.SetDefine(TEXT("MATERIAL_IS_SKY"), Material->IsSky());
OutEnvironment.SetDefine(TEXT("MATERIAL_COMPUTE_FOG_PER_PIXEL"), Material->ComputeFogPerPixel());
OutEnvironment.SetDefine(TEXT("MATERIAL_FULLY_ROUGH"), bIsFullyRough || Material->IsFullyRough());
OutEnvironment.SetDefine(TEXT("MATERIAL_USES_ANISOTROPY"), bUsesAnisotropy);
(......)
// 머티리얼(Material)파라미터 .
for (int32 CollectionIndex = 0; CollectionIndex < ParameterCollections.Num(); CollectionIndex++)
{
const FString CollectionName = FString::Printf(TEXT("MaterialCollection%u"), CollectionIndex);
FShaderUniformBufferParameter::ModifyCompilationEnvironment(*CollectionName, ParameterCollections[CollectionIndex]->GetUniformBufferStruct(), InPlatform, OutEnvironment);
}
OutEnvironment.SetDefine(TEXT("IS_MATERIAL_SHADER"), TEXT("1"));
// 처리(Process)shading model.
FMaterialShadingModelField ShadingModels = Material->GetShadingModels();
if (Material->IsShadingModelFromMaterialExpression() && ShadingModelsFromCompilation.IsValid())
{
ShadingModels = ShadingModelsFromCompilation;
}
if (ShadingModels.IsLit())
{
int NumSetMaterials = 0;
if (ShadingModels.HasShadingModel(MSM_DefaultLit))
{
OutEnvironment.SetDefine(TEXT("MATERIAL_SHADINGMODEL_DEFAULT_LIT"), TEXT("1"));
NumSetMaterials++;
}
if (ShadingModels.HasShadingModel(MSM_Subsurface))
{
OutEnvironment.SetDefine(TEXT("MATERIAL_SHADINGMODEL_SUBSURFACE"), TEXT("1"));
NumSetMaterials++;
}
if (ShadingModels.HasShadingModel(MSM_PreintegratedSkin))
{
OutEnvironment.SetDefine(TEXT("MATERIAL_SHADINGMODEL_PREINTEGRATED_SKIN"), TEXT("1"));
NumSetMaterials++;
}
if (ShadingModels.HasShadingModel(MSM_SubsurfaceProfile))
{
OutEnvironment.SetDefine(TEXT("MATERIAL_SHADINGMODEL_SUBSURFACE_PROFILE"), TEXT("1"));
NumSetMaterials++;
}
(......)
}
else
{
OutEnvironment.SetDefine(TEXT("MATERIAL_SINGLE_SHADINGMODEL"), TEXT("1"));
OutEnvironment.SetDefine(TEXT("MATERIAL_SHADINGMODEL_UNLIT"), TEXT("1"));
}
(......)
}
// 페치/가져오기(Fetch)머티리얼(Material)테이블 의 shader코드 . (패딩/채우기 MaterialTemplate%s의 코드 )
FString FHLSLMaterialTranslator::GetMaterialShaderCode()
{
// 레이턴시 MaterialTemplate.
FLazyPrintf LazyPrintf(*MaterialTemplate);
// 로 포인트 보간(Interpolation)할당(Allocate)슬롯(Slot).
FString VertexInterpolatorsOffsetsDefinition;
TBitArray<> FinalAllocatedCoords = GetVertexInterpolatorsOffsets(VertexInterpolatorsOffsetsDefinition);
const uint32 NumUserVertexTexCoords = GetNumUserVertexTexCoords();
const uint32 NumUserTexCoords = GetNumUserTexCoords();
const uint32 NumCustomVectors = FMath::DivideAndRoundUp((uint32)CurrentCustomVertexInterpolatorOffset, 2u);
const uint32 NumTexCoordVectors = FinalAllocatedCoords.FindLast(true) + 1;
LazyPrintf.PushParam(*FString::Printf(TEXT("%u"),NumUserVertexTexCoords));
LazyPrintf.PushParam(*FString::Printf(TEXT("%u"),NumUserTexCoords));
LazyPrintf.PushParam(*FString::Printf(TEXT("%u"),NumCustomVectors));
LazyPrintf.PushParam(*FString::Printf(TEXT("%u"),NumTexCoordVectors));
LazyPrintf.PushParam(*VertexInterpolatorsOffsetsDefinition);
FString MaterialAttributesDeclaration;
// 머티리얼(Material)의 포인트 타입/유형 시퀀스 HLSL타입/유형 .
const TArray<FGuid>& OrderedVisibleAttributes = FMaterialAttributeDefinitionMap::GetOrderedVisibleAttributeList();
for(const FGuid& AttributeID : OrderedVisibleAttributes)
{
const FString PropertyName = FMaterialAttributeDefinitionMap::GetAttributeName(AttributeID);
const EMaterialValueType PropertyType = FMaterialAttributeDefinitionMap::GetValueType(AttributeID);
switch (PropertyType)
{
case MCT_Float1: case MCT_Float: MaterialAttributesDeclaration += FString::Printf(TEXT("\tfloat %s;") LINE_TERMINATOR, *PropertyName); break;
case MCT_Float2: MaterialAttributesDeclaration += FString::Printf(TEXT("\tfloat2 %s;") LINE_TERMINATOR, *PropertyName); break;
case MCT_Float3: MaterialAttributesDeclaration += FString::Printf(TEXT("\tfloat3 %s;") LINE_TERMINATOR, *PropertyName); break;
case MCT_Float4: MaterialAttributesDeclaration += FString::Printf(TEXT("\tfloat4 %s;") LINE_TERMINATOR, *PropertyName); break;
case MCT_ShadingModel: MaterialAttributesDeclaration += FString::Printf(TEXT("\tuint %s;") LINE_TERMINATOR, *PropertyName); break;
}
}
LazyPrintf.PushParam(*MaterialAttributesDeclaration);
FString PixelMembersDeclaration;
FString NormalAssignment;
FString PixelMembersSetupAndAssignments;
// 페치/가져오기(Fetch)의 입력머티리얼(Material)코드 .
GetSharedInputsMaterialCode(PixelMembersDeclaration, NormalAssignment, PixelMembersSetupAndAssignments);
LazyPrintf.PushParam(*PixelMembersDeclaration);
LazyPrintf.PushParam(*ResourcesString);
if (bCompileForComputeShader)
{
LazyPrintf.PushParam(*GenerateFunctionCode(CompiledMP_EmissiveColorCS));
}
else
{
LazyPrintf.PushParam(TEXT("return 0"));
}
LazyPrintf.PushParam(*FString::Printf(TEXT("return %.5f"), Material->GetTranslucencyDirectionalLightingIntensity()));
LazyPrintf.PushParam(*FString::Printf(TEXT("return %.5f"), Material->GetTranslucentShadowDensityScale()));
LazyPrintf.PushParam(*FString::Printf(TEXT("return %.5f"), Material->GetTranslucentSelfShadowDensityScale()));
LazyPrintf.PushParam(*FString::Printf(TEXT("return %.5f"), Material->GetTranslucentSelfShadowSecondDensityScale()));
LazyPrintf.PushParam(*FString::Printf(TEXT("return %.5f"), Material->GetTranslucentSelfShadowSecondOpacity()));
LazyPrintf.PushParam(*FString::Printf(TEXT("return %.5f"), Material->GetTranslucentBackscatteringExponent()));
{
FLinearColor Extinction = Material->GetTranslucentMultipleScatteringExtinction();
LazyPrintf.PushParam(*FString::Printf(TEXT("return MaterialFloat3(%.5f, %.5f, %.5f)"), Extinction.R, Extinction.G, Extinction.B));
}
LazyPrintf.PushParam(*FString::Printf(TEXT("return %.5f"), Material->GetOpacityMaskClipValue()));
LazyPrintf.PushParam(*GenerateFunctionCode(MP_WorldPositionOffset));
LazyPrintf.PushParam(*GenerateFunctionCode(CompiledMP_PrevWorldPositionOffset));
LazyPrintf.PushParam(*GenerateFunctionCode(MP_WorldDisplacement));
LazyPrintf.PushParam(*FString::Printf(TEXT("return %.5f"), Material->GetMaxDisplacement()));
LazyPrintf.PushParam(*GenerateFunctionCode(MP_TessellationMultiplier));
LazyPrintf.PushParam(*GenerateFunctionCode(MP_CustomData0));
LazyPrintf.PushParam(*GenerateFunctionCode(MP_CustomData1));
// 패딩/채우기 정의 텍스처(Texture)좌표 할당(Allocate).
FString CustomUVAssignments;
int32 LastProperty = -1;
for (uint32 CustomUVIndex = 0; CustomUVIndex < NumUserTexCoords; CustomUVIndex++)
{
if (CustomUVIndex == 0)
{
CustomUVAssignments += TranslatedCodeChunkDefinitions[MP_CustomizedUVs0 + CustomUVIndex];
}
if (TranslatedCodeChunkDefinitions[MP_CustomizedUVs0 + CustomUVIndex].Len() > 0)
{
if (LastProperty >= 0)
{
check(TranslatedCodeChunkDefinitions[LastProperty].Len() == TranslatedCodeChunkDefinitions[MP_CustomizedUVs0 + CustomUVIndex].Len());
}
LastProperty = MP_CustomizedUVs0 + CustomUVIndex;
}
CustomUVAssignments += FString::Printf(TEXT("\tOutTexCoords[%u] = %s;") LINE_TERMINATOR, CustomUVIndex, *TranslatedCodeChunks[MP_CustomizedUVs0 + CustomUVIndex]);
}
LazyPrintf.PushParam(*CustomUVAssignments);
// 패딩/채우기 정의 포인트 셰이딩 보간(Interpolation)할당(Allocate).
FString CustomInterpolatorAssignments;
for (UMaterialExpressionVertexInterpolator* Interpolator : CustomVertexInterpolators)
{
if (Interpolator->InterpolatorOffset != INDEX_NONE)
{
const EMaterialValueType Type = Interpolator->InterpolatedType == MCT_Float ? MCT_Float1 : Interpolator->InterpolatedType;
const TCHAR* Swizzle[2] = { TEXT("x"), TEXT("y") };
const int32 Offset = Interpolator->InterpolatorOffset;
const int32 Index = Interpolator->InterpolatorIndex;
CustomInterpolatorAssignments += FString::Printf(TEXT("\tOutTexCoords[VERTEX_INTERPOLATOR_%i_TEXCOORDS_X].%s = VertexInterpolator%i(Parameters).x;") LINE_TERMINATOR, Index, Swizzle[Offset%2], Index);
if (Type >= MCT_Float2)
{
(......)
}
}
}
LazyPrintf.PushParam(*CustomInterpolatorAssignments);
// Normal의 초기화(Initialize)
LazyPrintf.PushParam(*TranslatedCodeChunkDefinitions[MP_Normal]);
LazyPrintf.PushParam(*NormalAssignment);
//는 의 을(를) 활용하여 코드 ,는 개 입력의
LazyPrintf.PushParam(*PixelMembersSetupAndAssignments);
LazyPrintf.PushParam(*FString::Printf(TEXT("%u"),MaterialTemplateLineNumber));
return LazyPrintf.GetResultString();
}이제 FHLSLMaterialTranslator의 몇 가지 중요한 인터페이스를 간략하게 요약합니다.
변환: 머티리얼 블루프린트의 머티리얼 노드 표현식을 변환하고 모든 머티리얼 속성의 컴파일된 결과를 그리드의 FShaderCodeChunk에 채웁니다.
GetMaterialEnvironment: 머티리얼 블루프린트 처리를 위한 컴파일 환경(매크로 정의)입니다.
GetMaterialShaderCode: MaterialTemplate.ush의 빈 코드, Translate에 따라 컴파일된 FShaderCodeChunk에 해당하는 머티리얼 속성 인터페이스, 기타 매크로 정의, 구조 및 도구 클래스 인터페이스를 입력합니다.
FHLSLMaterialTranslator로 컴파일한 후 전체 머티리얼 셰이더 코드가 얻어지며, 이는 컴파일을 위해 FMaterialShaderMap::Compile 인터페이스로 전송됩니다. 컴파일된 셰이더 코드는 FMaterialShaderMap에 저장됩니다.
FMaterial::BeginCompileShaderMap을 호출하기 위한 유일한 인터페이스는 FMaterial::CacheShaders입니다. FMaterial::CacheShaders를 호출하기 위한 핵심 호출 스택은 다음과 같습니다.
- UMaterial::CacheResourceShadersForRendering
UMaterial::CacheShadersForResources
FMaterial::캐시셰이더
- UMaterialInstance::CacheResourceShadersForRendering
UMaterialInstance::CacheShadersForResources
FMaterial::캐시셰이더
CacheResourceShadersForRendering에는 UMaterial 및 UMaterialInstance에 대한 다양한 설정 인터페이스를 포함하여 많은 호출 로직이 있습니다.
-SetMaterialUsage
- UpdateMaterialShaderCacheAndTextureReferences
-포스트로드
PostEditChangeProperty내부
ForceRecompileForRendering
AllMaterialsCacheResourceShadersForRendering
위의 인터페이스 중에서 PostLoad가 반드시 호출될 것입니다.
이 시점에서 자료 편집의 전체 과정은 명확합니다. 흐름도는 다음과 같습니다(UMaterial을 예로 들면 UMaterialInstance도 마찬가지입니다).
그래프 TD A1[UMaterial::SetMaterialUsage] --> B(UMaterial::CacheResourceShadersForRendering) A2[UMaterial::PostLoad] --> B(UMaterial::CacheResourceShadersForRendering) A3[UMaterial::ForceRecompileForRendering] --> B(UMerial::CacheResourceShadersForRendering) B --> C(UMerial::CacheShadersForResources) C --> C1(FMaterial::CacheShaders) C1 -->D(FMaterial::BeginCompileShaderMap) D -->E(FHLSLMaterialTranslator::번역) E -->F(FHLSLMaterialTranslator::GetMaterialEnvironment) F -->G(FHLSLMaterialTranslator::GetMaterialShaderCode) G -->H(FMaterialShaderMap::컴파일) H -->I (컴파일 결과 처리)
실제로 다양한 유형의 셰이더를 컴파일할 때 필요한 데이터는 정확히 동일하지 않습니다.
유형 구성
글로벌셰이더 Shader_x.usf
머티리얼셰이더 Shader_x.usf + MaterialTemplate_x.usf
MeshMaterialShader Shader_x.usf + MaterialTemplate_x.usf + VertexFactory_x.usf
그 중에는:
Shader_x.usf: DeferredLightVertexShaders.usf, DeferredLightPixelShaders.usf와 같은 엔진 셰이더 디렉터리의 기존 파일입니다.
MaterialTemplate_x.usf: FHLSLMaterialTranslator가 머티리얼 청사진을 컴파일한 후 MaterialTemplate.ush를 채우는 코드입니다.
VertexFactory_x.usf: 엔진 셰이더 디렉터리에 있는 기존 버텍스 팩토리 파일 코드(예: LocalVertexFactory.ush, GpuSkinVertexFactory.ush).
또한 동일한 머티리얼 블루프린트에 대해 서로 다른 셰이딩 빈도를 사용하여 셰이더 코드를 생성하는 데 필요한 데이터도 다릅니다.
착색 빈도 구성
픽셀셰이더 MaterialTemplate_x.usf + VertexFactory_x.usf + PixelShader_x.usf
버텍스 셰이더 MaterialTemplate_x.usf + VertexFactory_x.usf + VertexShader_x.usf
기하학셰이더 MaterialTemplate_x.usf + VertexFactory_x.usf + VertexShader_x.usf
그 중에는:
PixelShader_x.usf: BasePassPixelShader.usf, ShadowDepthPixelShader.usf, DepthOnlyPixelShader.usf와 같은 특정 렌더링 패스의 PixelShader 코드입니다.
VertexShader_x.usf: BasePassVertexShader.usf, ShadowDepthVertexShader.usf, DepthOnlyVertexShader.usf와 같은 렌더링 패스의 VertexShader 코드입니다.
또한 머티리얼 편집기에서 MaterialTemplate을 채운 후 각 대상 플랫폼에 대한 코드를 볼 수 있습니다.

아래 그림은 HLSL을 연 후의 코드 미리보기 인터페이스입니다.

9.4 재료 개발
이 장에서는 소재 개발 사례, 디버깅 기술 및 최적화 기술에 대해 설명합니다.
9.4.1 머티리얼 디버깅 및 최적화
머티리얼 및 머티리얼 인스턴스 편집기 모두에서 현재 머티리얼의 컴파일된 명령 수, 텍스처 샘플 수, 보간기 데이터 등을 볼 수 있습니다.

위: Material Editor의 통계 창; 하단: 머티리얼 인스턴스 에디터의 통계입니다.
머티리얼 에디터 상단에서 플랫폼 통계를 열면 지정된 플랫폼(PS, VS, 샘플러 등)에 대한 더 자세한 데이터를 볼 수 있습니다.

다른 플랫폼의 데이터를 보려면 위 그림의 설정 버튼을 클릭하세요.

재료 편집기의 자산/크기 맵 메뉴를 열어 재료 리소스의 공간(디스크 및 메모리) 레이아웃 맵을 엽니다.

재료 편집기의 Windows / 개발자 도구 / 재료 분석기 메뉴를 열면 지정된 재료의 수준과 다양한 유형의 속성 수를 볼 수 있습니다.

일부 데이터가 비정상이거나 개선될 수 있는 경우 시스템은 수정 제안을 알리고 제공합니다. 예를 들어, 다음 그림은 동일한 배열을 가진 여러 재료가 있음을 보여줍니다.

로컬 컬렉션 생성을 클릭하여 모든 관련 인스턴스를 로컬 컬렉션에 배치하면 보다 효율적인 머티리얼 매개변수 설정을 위해 쉽게 찾고 업데이트할 수 있습니다.
물론 위의 재료 분석기는 한 번에 하나의 재료만 수동으로 선택할 수 있으므로 속도가 매우 느립니다. 실제로 이를 기반으로 배치 검사 도구를 개발할 수 있으며 일부 규칙을 사용자 정의하여 로컬 매개변수 세트를 자동으로 생성하여 재료 최적화의 효율성과 효과를 향상시킬 수도 있습니다.
또한 머티리얼 블루프린트를 편집할 때 다음 사항에 주의할 수 있습니다.
일반적으로 순열 수를 증가시키는 정적 변수, 스위치, 레벨 및 FeatureLevel의 수에 주의하십시오.
불필요한 재료 노드를 줄이거나 피하기 위해 재료 매개변수를 병합합니다.
주석을 합리적으로 추가하고 재료 기능에 모듈화 및 추상화합니다. 이는 복잡한 재료에 매우 필요하며 유지 관리 및 확장에 도움이 되며 재료 재사용률을 향상시킵니다.
대부분의 머티리얼 노드는 PS 노드이지만 아래와 같이 소수의 VS 노드도 있습니다.

특히 VertexInterpolator 노드에서는 논리의 일부를 VS 계산에 넣은 다음 VertexInterpolator를 사용하여 하드웨어 보간을 수행한 다음 PS 값을 출력할 수 있습니다.

VertexInterpolator는 Float~Float4 보간을 지원하며 최대 4개의 Float4 보간을 지원합니다. 현재 VertexInterpolator 수를 알고 싶다면 Stats 창에서 확인할 수 있습니다.

또한 셰이더 스테이지 스위치는 VS와 PS의 값을 다르게 처리할 수도 있습니다.

9.4.2 소재 개발 사례
9.4.2.1 머티리얼 노드 추가
이 섹션에서는 MyCustomOperation이라는 머티리얼 노드 추가를 예로 들어 보겠습니다.
먼저 FMaterialCompiler 및 FHLSLMaterialTranslator에 관련 인터페이스와 구현을 추가해야 합니다.
// Engine\Source\Runtime\Engine\Public\MaterialCompiler.h
class FMaterialCompiler
{
public:
virtual int32 MyCustomOperation(int32 A, int32 B) { return 0; } // 추상화 인터페이스 , .
(......)
};
// Engine\Source\Runtime\Engine\Private\Materials\HLSLMaterialTranslator.h
class FHLSLMaterialTranslator : public FMaterialCompiler
{
public:
virtual int32 MyCustomOperation(int32 A, int32 B) override;
(......)
};
// Engine\Source\Runtime\Engine\Private\Materials\HLSLMaterialTranslator.cpp
int32 FHLSLMaterialTranslator::MyCustomOperation(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))
{
// 는 정의 의 포인트 , 로써 HLSL의 코드 ^_^
return AddUniformExpressionWithHash(Hash, new FMaterialUniformExpressionFoldedMath(GetParameterUniformExpression(A),GetParameterUniformExpression(B),FMO_Add),GetArithmeticResultType(A,B),TEXT("(%s + %s * 0.5)"),*GetParameterCode(A),*GetParameterCode(B));
}
else
{
return AddCodeChunkWithHash(Hash, GetArithmeticResultType(A,B),TEXT("(%s + %s * 0.5)"),*GetParameterCode(A),*GetParameterCode(B));
}
}위 인터페이스를 구현한 후, 해당 머티리얼 표현식 유형과 파일을 추가해야 합니다:
// Engine\Source\Runtime\Engine\Classes\Materials\MaterialExpressionMyCustomOperation.h
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "MaterialExpressionIO.h"
#include "Materials/MaterialExpression.h"
// UBT의 , .
#include "MaterialExpressionMyCustomOperation.generated.h"
UCLASS(MinimalAPI)
class UMaterialExpressionMyCustomOperation : public UMaterialExpression
{
GENERATED_UCLASS_BODY()
UPROPERTY(meta = (RequiredInput = "false", ToolTip = "Defaults to 'ConstA' if not specified"))
FExpressionInput A;
UPROPERTY(meta = (RequiredInput = "false", ToolTip = "Defaults to 'ConstB' if not specified"))
FExpressionInput B;
UPROPERTY(EditAnywhere, Category=MaterialExpressionAdd, meta=(OverridingInputProperty = "A"))
float ConstA;
UPROPERTY(EditAnywhere, Category=MaterialExpressionAdd, meta=(OverridingInputProperty = "B"))
float ConstB;
//~ Begin UMaterialExpression Interface
#if WITH_EDITOR
virtual int32 Compile(class FMaterialCompiler* Compiler, int32 OutputIndex) override;
virtual void GetCaption(TArray<FString>& OutCaptions) const override;
#endif // WITH_EDITOR
//~ End UMaterialExpression Interface
};
// Engine\Source\Runtime\Engine\Private\Materials\MaterialExpressions.cpp
// 추가(Add)포인트 의 을(를) 활용하여 .
#include "Materials/MaterialExpressionMyCustomOperation.h"
// 기본값(Default)함수 .
UMaterialExpressionMyCustomOperation::UMaterialExpressionMyCustomOperation(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
// Structure to hold one-time initialization
struct FConstructorStatics
{
FText NAME_Math;
FConstructorStatics()
: NAME_Math(LOCTEXT( "Math", "Math" ))
{
}
};
static FConstructorStatics ConstructorStatics;
ConstA = 0.0f;
ConstB = 1.0f;
#if WITH_EDITORONLY_DATA
MenuCategories.Add(ConstructorStatics.NAME_Math);
#endif
}
// .
int32 UMaterialExpressionMyCustomOperation::Compile(class FMaterialCompiler* Compiler, int32 OutputIndex)
{
int32 Arg1 = A.GetTracedInput().Expression ? A.Compile(Compiler) : Compiler->Constant(ConstA);
int32 Arg2 = B.GetTracedInput().Expression ? B.Compile(Compiler) : Compiler->Constant(ConstB);
return Compiler->MyCustomOperation(Arg1, Arg2);
}
// .
void UMaterialExpressionMyCustomOperation::GetCaption(TArray<FString>& OutCaptions) const
{
FString ret = TEXT("MyCustomOperation");
FExpressionInput ATraced = A.GetTracedInput();
FExpressionInput BTraced = B.GetTracedInput();
if(!ATraced.Expression || !BTraced.Expression)
{
ret += TEXT("(");
ret += ATraced.Expression ? TEXT(",") : FString::Printf( TEXT("%.4g,"), ConstA);
ret += BTraced.Expression ? TEXT(")") : FString::Printf( TEXT("%.4g)"), ConstB);
}
OutCaptions.Add(ret);
}위 코드를 추가한 후 엔진 코드를 컴파일하고 UE 에디터를 시작한 다음 머티리얼 에디터를 열고 노드에서 MyCustomOperation 머티리얼 노드를 검색하여 사용할 수 있습니다(아래 그림).

9.4.2.2 사용자 정의 노드 확장
UE와 함께 제공되는 커스텀 머티리얼 노드는 어떤 코드라도 작성할 수 있지만 특정 기능으로 제한됩니다. 예를 들어 다음 코드는 Custom으로 작성되었습니다.
return 1.0;실제로 HLSL 컴파일러는 이를 다음과 유사한 코드로 컴파일합니다.
float CustomExpression0()
{
return 1.0;
}함수 본문으로 제한되면 성능이 크게 제한됩니다. 예를 들어, 사용자 정의 노드에서는 기능과 구조를 정의할 수 없으며 다른 ush 파일을 참조할 수도 없습니다.
이를 수행하는 일반적이고 빠른 방법은 Custom 단계 전후에 중괄호를 추가하는 것입니다.
return 0.0; } // 매칭 의 {
float MyParameter = 1.0;
// 의 코드 .
float MyFunction()
{
return MyParameter;
// }, 이므로생성된 HLSL 코드는 다음과 같습니다.
float CustomExpression0()
{
return 1.0;
}
float MyParameter = 1.0;
float MyFunction()
{
return MyParameter;
}하지만 이 방법은 매우 보기 흉하며 머티리얼 노드에서 얻은 결과가 첫 번째 반환 값입니다. 다음은 여러 함수와 변수의 정의를 지원하는 약간 더 우아한 방법입니다.
struct FMyStruct
{
float3 ColorDensity;
float3 ColorOperation(float3 Color)
{
return Color * ColorDensity;
}
float3 Out()
{
// InColor는 함수 입력파라미터 .
return ColorOperation(InColor);
}
};
FMyStruct MyStruct;
FMyStruct.ColorDensity = float3(0.5, 0.5, 0);
return MyStruct.Out();위의 코드는 함수 본문 내에서 로컬 구조를 정의할 수 있는 HLSL의 기능을 활용하여 다양한 함수 및 변수의 정의를 지원합니다.
9.4.2.3 확장된 자료 템플릿
MaterialTemplate.ush는 수많은 전역 매크로, 구조 및 인터페이스를 정의합니다. 물론 이를 수정하고 다음과 같이 필요한 코드나 데이터를 추가할 수도 있습니다.
전역 정적 변수를 추가합니다.
매크로 정의가 추가되었습니다.
참조 파일을 추가합니다.
HLSL 컴파일러에서 채우려면 %s 간격 코드를 추가하세요.
구조를 추가합니다.
사용자 정의 기능을 추가합니다.
위의 모든 데이터와 인터페이스는 커스텀 노드 및 MaterialFunction과 결합되어 머티리얼 블루프린트 액세스에 노출될 수 있습니다.
예를 들어 MaterialTemplate.ush에 전역 정적 상수를 추가해 보겠습니다.
// Engine\Shaders\Private\MaterialTemplate.ush
// include.
static const float MyGlobalParameter = 0.5;엔진을 컴파일하고, 머티리얼 에디터를 시작한 후 열고, 새 머티리얼 함수를 추가하고, 머티리얼 함수에 Custom 노드를 추가합니다. Custom 노드의 코드는 다음과 같습니다.
return MyGlobalParameter;그런 다음 머티리얼 함수의 라이브러리에 노출을 확인하세요.

이런 방식으로 머티리얼 노드에서 머티리얼 함수를 검색하고 적용할 수 있습니다:

9.5 이 기사 요약
이 문서에서는 주로 UE 머티리얼 시스템의 기본 개념, 유형 및 메커니즘을 설명합니다. 이 글을 읽은 후 아이들이 더 이상 UE의 자료를 낯설지 않고 쉽게 익히고, 적용하고, 확장할 수 있기를 바랍니다.
9.5.1 이 기사에 대한 생각
평소와 마찬가지로 이 기사에서는 UE 머티리얼 시스템을 숙달하고 이해하는 데 도움이 되는 몇 가지 작은 생각도 정리했습니다.
UMaterialInterface, FMaterialRenderProxy 및 FMaterial의 관계와 차이점은 무엇입니까? 재료의 종류가 왜 이렇게 많나요?
머티리얼 렌더링 중 데이터 업데이트 과정은 어떻게 되나요?
재료 설계도를 작성하는 과정은 어떻게 되나요? 컴파일러의 주요 인터페이스 기능은 무엇입니까?
자료 편집 시 주의해야 할 연주 데이터는 무엇인가요? 확인하는 방법?
2차원 스타일 렌더링을 지원하는 새로운 셰이딩 모델이 추가되었습니다.
특별 지침
모든 참고문헌의 저자에게 감사드립니다. 일부 사진은 참고 자료와 인터넷에서 가져온 것이므로 삭제되었습니다.
이 시리즈의 기사는 저자가 직접 작성한 것이며 블로그에만 게시됩니다. 이 글의 링크를 공유하셔도 좋지만 무단 전재는 허용되지 않습니다!
계속되는 일련의 기사. 전체 목록을 보려면 콘텐츠 개요를 클릭하세요.
계속되는 일련의 기사. 전체 목록을 보려면 콘텐츠 개요를 클릭하세요.
계속되는 일련의 기사. 전체 목록을 보려면 콘텐츠 개요를 클릭하세요.
📚 참고 자료 및 공식 링크 (References)
- Unreal Engine Source
- Rendering and Graphics
- Materials
- Graphics Programming
- UE4렌더링
- UE4 Render System Sheet
- 【UE4 Renderer】
<03>PipelineBase - UE4머티리얼 시스템소스 코드 분석UMaterial 및 머티리얼소개
- UE4머티리얼 시스템소스 코드 분석머티리얼HLSL CODE
- UE4 HLSL 및 Shader 개발가이드 및
- Material Analyzer
- Unreal Engine4 Custom Function
- Unreal Engine 4 Rendering Part 6: Adding a new Shading Model