PART 11 · 강의 3/7

Substrate Materials

UE 5.7의 차세대 모듈식 물리 기반 머티리얼 시스템

01

Substrate 머티리얼 시스템

고정 셰이딩 모델을 대체하는 모듈식 프레임워크

Substrate는 UE 5.7에서 Production Ready 상태로 기본 활성화된 차세대 머티리얼 시스템입니다. 기존의 고정 셰이딩 모델(Default Lit, Subsurface, Clear Coat 등)을 대체하여 다양한 물리적 동작을 하나의 머티리얼에서 조합할 수 있습니다.

Substrate의 핵심 장점
  • 모듈식 설계: 금속, 클리어 코트, 피부, 천 등을 자유롭게 조합
  • 물리적 정확성: 에너지 보존 법칙 준수
  • 스케일러빌리티: 데스크톱부터 모바일까지 지원
  • 레이어드 머티리얼: 기본 블렌딩 지원

GBuffer 경로 비교

Adaptive GBuffer

각 픽셀에 필요한 만큼만 데이터를 저장하여 메모리 최적화

  • 단순 머티리얼: 적은 데이터 사용
  • 복잡한 머티리얼: 필요한 만큼 확장
  • 성능 자동 최적화

Blendable GBuffer

머티리얼 간 부드러운 블렌딩을 위한 고정 포맷

  • 데칼과의 호환성 향상
  • 프록시미티 페이드 지원
  • 일관된 블렌딩 품질
02

Substrate 머티리얼 생성

C++에서 프로시저럴 Substrate 머티리얼 제어

C++
// SubstrateMaterialManager.h #pragma once #include "CoreMinimal.h" #include "Materials/MaterialInstanceDynamic.h" #include "SubstrateMaterialManager.generated.h" /** * Substrate 머티리얼 동적 생성 및 제어 * 런타임에 Substrate 레이어를 조작합니다. */ USTRUCT(BlueprintType) struct FSubstrateLayerParams { GENERATED_BODY() // 베이스 컬러 UPROPERTY(EditAnywhere, BlueprintReadWrite) FLinearColor BaseColor = FLinearColor::White; // 메탈릭 UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ClampMin = "0.0", ClampMax = "1.0")) float Metallic = 0.0f; // 스페큘러 UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ClampMin = "0.0", ClampMax = "1.0")) float Specular = 0.5f; // 러프니스 UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ClampMin = "0.0", ClampMax = "1.0")) float Roughness = 0.5f; // 서브서페이스 (피부, 왁스 등) UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ClampMin = "0.0", ClampMax = "1.0")) float Subsurface = 0.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite) FLinearColor SubsurfaceColor = FLinearColor::Red; // 클리어 코트 UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ClampMin = "0.0", ClampMax = "1.0")) float ClearCoat = 0.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ClampMin = "0.0", ClampMax = "1.0")) float ClearCoatRoughness = 0.1f; // 애니소트로피 (헤어, 브러시드 메탈) UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ClampMin = "-1.0", ClampMax = "1.0")) float Anisotropy = 0.0f; // 천 셰이딩 UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ClampMin = "0.0", ClampMax = "1.0")) float Cloth = 0.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite) FLinearColor FuzzColor = FLinearColor::White; }; UCLASS(BlueprintType) class MYGAME_API USubstrateMaterialManager : public UObject { GENERATED_BODY() public: // Substrate 다이나믹 머티리얼 생성 UFUNCTION(BlueprintCallable, Category = "Substrate") static UMaterialInstanceDynamic* CreateSubstrateMaterial( UObject* WorldContext, UMaterialInterface* BaseMaterial, const FSubstrateLayerParams& Params); // 레이어 파라미터 업데이트 UFUNCTION(BlueprintCallable, Category = "Substrate") static void UpdateSubstrateLayer( UMaterialInstanceDynamic* MaterialInstance, const FSubstrateLayerParams& Params); // 레이어 블렌딩 UFUNCTION(BlueprintCallable, Category = "Substrate") static void BlendSubstrateLayers( UMaterialInstanceDynamic* MaterialInstance, const FSubstrateLayerParams& LayerA, const FSubstrateLayerParams& LayerB, float BlendFactor); };
C++
// SubstrateMaterialManager.cpp #include "SubstrateMaterialManager.h" #include "Materials/MaterialInstanceDynamic.h" UMaterialInstanceDynamic* USubstrateMaterialManager::CreateSubstrateMaterial( UObject* WorldContext, UMaterialInterface* BaseMaterial, const FSubstrateLayerParams& Params) { if (!BaseMaterial || !WorldContext) { UE_LOG(LogTemp, Warning, TEXT("Invalid parameters for CreateSubstrateMaterial")); return nullptr; } // 다이나믹 머티리얼 인스턴스 생성 UMaterialInstanceDynamic* MID = UMaterialInstanceDynamic::Create(BaseMaterial, WorldContext); if (MID) { UpdateSubstrateLayer(MID, Params); } return MID; } void USubstrateMaterialManager::UpdateSubstrateLayer( UMaterialInstanceDynamic* MaterialInstance, const FSubstrateLayerParams& Params) { if (!MaterialInstance) return; // 기본 파라미터 설정 MaterialInstance->SetVectorParameterValue( FName("BaseColor"), Params.BaseColor); MaterialInstance->SetScalarParameterValue( FName("Metallic"), Params.Metallic); MaterialInstance->SetScalarParameterValue( FName("Specular"), Params.Specular); MaterialInstance->SetScalarParameterValue( FName("Roughness"), Params.Roughness); // Substrate 특화 파라미터 MaterialInstance->SetScalarParameterValue( FName("Subsurface"), Params.Subsurface); MaterialInstance->SetVectorParameterValue( FName("SubsurfaceColor"), Params.SubsurfaceColor); MaterialInstance->SetScalarParameterValue( FName("ClearCoat"), Params.ClearCoat); MaterialInstance->SetScalarParameterValue( FName("ClearCoatRoughness"), Params.ClearCoatRoughness); MaterialInstance->SetScalarParameterValue( FName("Anisotropy"), Params.Anisotropy); MaterialInstance->SetScalarParameterValue( FName("Cloth"), Params.Cloth); MaterialInstance->SetVectorParameterValue( FName("FuzzColor"), Params.FuzzColor); } void USubstrateMaterialManager::BlendSubstrateLayers( UMaterialInstanceDynamic* MaterialInstance, const FSubstrateLayerParams& LayerA, const FSubstrateLayerParams& LayerB, float BlendFactor) { if (!MaterialInstance) return; BlendFactor = FMath::Clamp(BlendFactor, 0.0f, 1.0f); // 선형 보간으로 파라미터 블렌딩 FSubstrateLayerParams BlendedParams; BlendedParams.BaseColor = FMath::Lerp(LayerA.BaseColor, LayerB.BaseColor, BlendFactor); BlendedParams.Metallic = FMath::Lerp(LayerA.Metallic, LayerB.Metallic, BlendFactor); BlendedParams.Specular = FMath::Lerp(LayerA.Specular, LayerB.Specular, BlendFactor); BlendedParams.Roughness = FMath::Lerp(LayerA.Roughness, LayerB.Roughness, BlendFactor); BlendedParams.Subsurface = FMath::Lerp(LayerA.Subsurface, LayerB.Subsurface, BlendFactor); BlendedParams.SubsurfaceColor = FMath::Lerp(LayerA.SubsurfaceColor, LayerB.SubsurfaceColor, BlendFactor); BlendedParams.ClearCoat = FMath::Lerp(LayerA.ClearCoat, LayerB.ClearCoat, BlendFactor); BlendedParams.ClearCoatRoughness = FMath::Lerp(LayerA.ClearCoatRoughness, LayerB.ClearCoatRoughness, BlendFactor); BlendedParams.Anisotropy = FMath::Lerp(LayerA.Anisotropy, LayerB.Anisotropy, BlendFactor); BlendedParams.Cloth = FMath::Lerp(LayerA.Cloth, LayerB.Cloth, BlendFactor); BlendedParams.FuzzColor = FMath::Lerp(LayerA.FuzzColor, LayerB.FuzzColor, BlendFactor); UpdateSubstrateLayer(MaterialInstance, BlendedParams); }
03

레이어 조합 예제

다양한 머티리얼 타입 구현

자동차 페인트 (클리어 코트 + 메탈릭)

베이스 메탈릭 레이어 위에 투명한 클리어 코트 적용

캐릭터 피부 (서브서페이스 + 스페큘러)

빛이 피부 내부를 투과하는 효과 + 자연스러운 반사

벨벳 옷감 (천 셰이딩 + 퍼지 컬러)

가장자리에서 밝아지는 림 라이팅 효과

C++
// 자동차 페인트 머티리얼 생성 예제 void ACarActor::SetupCarPaintMaterial(FLinearColor PaintColor) { FSubstrateLayerParams CarPaintParams; // 베이스 페인트 레이어 CarPaintParams.BaseColor = PaintColor; CarPaintParams.Metallic = 0.9f; // 메탈릭 페인트 CarPaintParams.Roughness = 0.3f; // 약간의 러프니스 CarPaintParams.Specular = 0.5f; // 클리어 코트 레이어 CarPaintParams.ClearCoat = 1.0f; // 완전한 클리어 코트 CarPaintParams.ClearCoatRoughness = 0.02f; // 매우 매끄러움 // 머티리얼 적용 CarPaintMID = USubstrateMaterialManager::CreateSubstrateMaterial( this, CarPaintBaseMaterial, CarPaintParams); CarMesh->SetMaterial(0, CarPaintMID); } // 캐릭터 피부 머티리얼 생성 예제 void ACharacterActor::SetupSkinMaterial() { FSubstrateLayerParams SkinParams; // 피부 베이스 SkinParams.BaseColor = FLinearColor(0.8f, 0.6f, 0.5f); SkinParams.Metallic = 0.0f; // 비금속 SkinParams.Roughness = 0.4f; // 적당한 러프니스 // 서브서페이스 스캐터링 SkinParams.Subsurface = 0.8f; // 강한 SSS 효과 SkinParams.SubsurfaceColor = FLinearColor(1.0f, 0.2f, 0.1f); // 붉은 내부 SkinMID = USubstrateMaterialManager::CreateSubstrateMaterial( this, SkinBaseMaterial, SkinParams); CharacterMesh->SetMaterial(SkinMaterialIndex, SkinMID); } // 벨벳 옷감 머티리얼 생성 예제 void ACharacterActor::SetupVelvetMaterial(FLinearColor FabricColor) { FSubstrateLayerParams VelvetParams; // 천 베이스 VelvetParams.BaseColor = FabricColor; VelvetParams.Metallic = 0.0f; VelvetParams.Roughness = 0.8f; // 높은 러프니스 // 천 셰이딩 (림 라이팅 효과) VelvetParams.Cloth = 1.0f; // 완전한 천 셰이딩 VelvetParams.FuzzColor = FLinearColor( // 가장자리에서 밝아지는 색 FabricColor.R * 1.5f, FabricColor.G * 1.5f, FabricColor.B * 1.5f ); VelvetMID = USubstrateMaterialManager::CreateSubstrateMaterial( this, ClothBaseMaterial, VelvetParams); CharacterMesh->SetMaterial(ClothMaterialIndex, VelvetMID); }
04

플랫폼별 스케일다운

모바일까지 지원하는 스케일러블 설계

Substrate는 모바일 플랫폼까지 스케일다운이 가능합니다. 각 플랫폼의 성능에 맞게 기능을 자동 조절합니다.

C++
// SubstratePlatformScaler.h #pragma once #include "CoreMinimal.h" #include "SubstratePlatformScaler.generated.h" UENUM(BlueprintType) enum class ESubstrateQualityLevel : uint8 { Low, // 모바일 저사양 Medium, // 모바일 고사양, 콘솔 저사양 High, // 콘솔 고사양, PC 저사양 Ultra // PC 고사양 }; UCLASS() class MYGAME_API USubstratePlatformScaler : public UObject { GENERATED_BODY() public: // 현재 플랫폼에 맞는 품질 레벨 가져오기 UFUNCTION(BlueprintPure, Category = "Substrate") static ESubstrateQualityLevel GetPlatformQualityLevel(); // 품질 레벨에 따른 파라미터 스케일링 UFUNCTION(BlueprintCallable, Category = "Substrate") static FSubstrateLayerParams ScaleParamsForQuality( const FSubstrateLayerParams& OriginalParams, ESubstrateQualityLevel QualityLevel); }; // SubstratePlatformScaler.cpp #include "SubstratePlatformScaler.h" #include "GenericPlatform/GenericPlatformMisc.h" ESubstrateQualityLevel USubstratePlatformScaler::GetPlatformQualityLevel() { #if PLATFORM_ANDROID || PLATFORM_IOS // 모바일 플랫폼 int32 MemoryMB = FPlatformMemory::GetPhysicalGBRam() * 1024; if (MemoryMB < 4096) return ESubstrateQualityLevel::Low; else return ESubstrateQualityLevel::Medium; #elif PLATFORM_DESKTOP // 데스크톱 - GPU 체크 FString GPUBrand = FPlatformMisc::GetPrimaryGPUBrand(); // 실제 구현에서는 더 정교한 GPU 성능 체크 필요 return ESubstrateQualityLevel::Ultra; #else // 콘솔 return ESubstrateQualityLevel::High; #endif } FSubstrateLayerParams USubstratePlatformScaler::ScaleParamsForQuality( const FSubstrateLayerParams& OriginalParams, ESubstrateQualityLevel QualityLevel) { FSubstrateLayerParams ScaledParams = OriginalParams; switch (QualityLevel) { case ESubstrateQualityLevel::Low: // 모바일 저사양: SSS, 클리어코트, 천 셰이딩 비활성화 ScaledParams.Subsurface = 0.0f; ScaledParams.ClearCoat = 0.0f; ScaledParams.Cloth = 0.0f; ScaledParams.Anisotropy = 0.0f; break; case ESubstrateQualityLevel::Medium: // 클리어코트만 비활성화 ScaledParams.ClearCoat = 0.0f; ScaledParams.Subsurface *= 0.5f; // SSS 약화 break; case ESubstrateQualityLevel::High: // 대부분의 기능 유지, 약간의 최적화 ScaledParams.ClearCoat *= 0.8f; break; case ESubstrateQualityLevel::Ultra: // 원본 그대로 사용 break; } return ScaledParams; }
SUMMARY

핵심 요약

  • Substrate는 UE 5.7에서 Production Ready로 기본 활성화
  • 고정 셰이딩 모델을 대체하는 모듈식 물리 기반 시스템
  • 금속, 클리어 코트, 피부, 천 등 다양한 속성을 하나의 머티리얼에서 조합
  • Adaptive GBufferBlendable GBuffer 두 가지 경로 지원
  • 모바일까지 스케일다운 가능한 설계
PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: Substrate 머티리얼 기본 생성

Substrate 머티리얼 모드를 활성화하고, BSDF 노드를 사용하여 RPG 갑옷(금속+천 혼합)의 복합 머티리얼을 생성하세요. 기존 머티리얼 그래프와의 차이를 비교하세요.

실습 2: 레이어 블렌딩 머티리얼

Substrate의 Horizontal/Vertical Layering을 사용하여 코팅된 금속(금속 베이스 + 클리어 코팅 레이어)이나 먼지 쌓인 돌 머티리얼을 구현하세요.

심화 과제: 오픈월드 Substrate 머티리얼 전략

오픈월드 RPG의 다양한 머티리얼(랜드스케이프, 건물, 캐릭터, 무기)을 Substrate로 마이그레이션하는 전략을 수립하세요. 성능 영향을 프로파일링하고, 기존 머티리얼과의 호환성을 테스트하세요.