UE5

[UE5]Custom Shader : PART1_Adding shading models_1

kaynine 2023. 9. 19. 15:31

시작하며

UE의 DefaultLit은 물리 기반 셰이더로서 몇몇 파라미터 수치의 제어만으로도 훌륭한 결과물을 만들 수 있는 셰이더이지만 카툰 스타일 같은 NPR(Non-Photorealistic Rendering)에는 맞지 않아 때때로 커스텀 셰이더를 제작할 필요가 있는데요. UE에서는 두 가지 방법으로 커스텀 셰이더를 작성할 수 있습니다. 한 가지는 셰이딩 모델을 추가하는 것이고 다른 하나는 글로벌 셰이더를 제작하는 건데 먼저 셰이딩 모델을 추가하는 것부터 살펴 보겠습니다.

 

셰이딩 모델 추가에는 크게 두 종류의 코드를 수정하거나 추가할 필요가 있는데요.

  • 엔진 소스 코드(*.cpp, *.h)
  • 셰이더 코드(*.ush, *usf)

이중에 엔진 소스 코드는 렌더링에 필요한 데이터나 파라미터들을 구조화하고 셰이더에 넘겨주는 역할과 셰이더 코드들을 컴파일하거나 직접 셰이더 코드를 생성(AutogenShaderHeaders.ush 같은)하는 등의 역할을 해요. 다른 하나인 셰이더 코드는 확장자를 보면 뭔가 UE만의 독자적인 형식일 것 같지만 들여다보면 그냥 HLSL 문법으로 작성된 코드들입니다.

 

따라서 디버깅에 필요한 지루한 소스 코드 빌드와 셰이더 컴파일 시간만 견딜 수 있다면 커스텀 셰이딩 모델을 추가하는 일이 그렇게 어렵지는 않지만 엔진 커스터마이징에 대한 공식 문서같은 것이 없어 여기 저기 흩어져 있는 코드를 읽어가며 흐름을 파악하는 것은 꽤 피곤한 일이 될 수 있습니다. 특히 UE5 부터 렌더링 과정에 추가된 루멘이나 버추얼 섀도 맵(VSM) 등과 관련된 새로운 기능들로 인해 렌더링 옵션에 따라 기본적인 흐름에서 벗어나 분기되고 토글되기도 하기 때문에 어떤 렌더링 기능들을 쓸 것인지, 어떤 스타일의 셰이딩을 수행할 것인지에 대한 구체적인 스펙이 있어야 정리돼 있어야 시간 낭비를 줄일 수 있어요. 그럼 먼저 소스 코드를 수정해서 셰이딩 모델을 추가해 볼 거에요.

 

UE 5.x를 기준으로 작성할 거에요. 4.x랑 크게 차이가 않지만 포스트가 진행될 수록 5.x 이후로 추가된 기능으로 인해 달라지는 부분이 생길 수 있어요. UE의 전체 소스 코드는 GitHub에서 압축 파일을 다운로드 받거나 프로젝트를 포킹한 다음 사용할 버전의 브랜치를 클로닝하면 돼요. 참고


엔진 코드 수정하기

EngineTypes.h

당연하게도 가장 먼저 할 일은 셰이딩 모델을 선언하는 거에요

enum EMaterialShadingModel : int
{
	// [...]
	
	// [ CUSTOM ] Adding custom shading model
	MSM_CustomLit UMETA(DisplayName = "Custom Lit"),
	
	// [...]
};

ShaderMaterial.h

다음으로는 셰이딩 모델의 속성 플래그를 선언하고요

struct FShaderMaterialPropertyDefines
{
	// [...]
	
	// [ CUSTOM ] Adding custom shading model
	uint8 MATERIAL_SHADINGMODEL_CUSTOM_LIT : 1;
	
	// [...]
}

MaterialExpressionShadingModel.h

머티리얼 에디터나 BP 에디터에서도 노출되도록 해줍니다

class UMaterialExpressionShadingModel : public UMaterialExpression
{
	// [...]
	
	// [ CUSTOM ] Adding custom shading model
	UPROPERTY(EditAnywhere, Category=ShadingModel, meta=(ValidEnumValues="MSM_DefaultLit, MSM_Subsurface, MSM_PreintegratedSkin, MSM_ClearCoat, MSM_SubsurfaceProfile, MSM_TwoSidedFoliage, MSM_Hair, MSM_Cloth, MSM_Eye, MSM_CustomLit", ShowAsInputPin = "Primary"))
		
	// [...]
}

MaterialShader.cpp

셰이더 컴파일러에 커스텀 셰이딩 모델의 이름을 알려주고요

FString GetShadingModelString(EMaterialShadingModel ShadingModel)
{
	FString ShadingModelName;
	switch(ShadingModel)
	{
		// [...]
		
		// [ CUSTOM ] Adding custom shading model
		case MSM_CustomLit:ShadingModelName = TEXT("MSM_CustomLit"); break;
		
		// [...]
	}
	return ShadingModelName;
}

void UpdateMaterialShaderCompilingStats(const FMaterial* Material)
{
	// [...]
	
	// [ CUSTOM ] Adding custom shading model
	if (ShadingModels.HasOnlyShadingModel(MSM_Unlit))
	{
		INC_DWORD_STAT_BY(STAT_ShaderCompiling_NumUnlitMaterialShaders, 1);
	}
	else if (ShadingModels.HasAnyShadingModel({ MSM_DefaultLit, MSM_Subsurface, MSM_PreintegratedSkin, MSM_ClearCoat, MSM_Cloth, MSM_SubsurfaceProfile, MSM_TwoSidedFoliage, MSM_SingleLayerWater, MSM_ThinTranslucent /*CUSTOM*/, MSM_CustomLit}))
	{
		INC_DWORD_STAT_BY(STAT_ShaderCompiling_NumLitMaterialShaders, 1);
	}

	// [...]
}

ShaderGenerationUtil.cpp

셰이더 컴파일러 환경을 설정 합니다

void FShaderCompileUtilities::ApplyFetchEnvironment(FShaderMaterialPropertyDefines& SrcDefines, FShaderCompilerEnvironment& OutEnvironment)
{
	// [...]
	
	// [ CUSTOM ] Adding custom shading model
	FETCH_COMPILE_BOOL(MATERIAL_SHADINGMODEL_CUSTOM_LIT);
	
	// [...]
}

HLSLMaterialTranslator.cpp

머티리얼의 환경 설정값들입니다.

void FHLSLMaterialTranslator::GetMaterialEnvironment(EShaderPlatform InPlatform, FShaderCompilerEnvironment& OutEnvironment)
{
	// [...]

	if (ShadingModels.IsLit())
	{
		// [...]
		
		// [ CUSTOM ] Adding custom shading model
		if (ShadingModels.HasShadingModel(MSM_CustomLit))
		{
			OutEnvironment.SetDefine(TEXT("MATERIAL_SHADINGMODEL_CUSTOM_LIT"), TEXT("1"));
			NumSetMaterials++;
		}
		
		// [...]
	}
	
	// [...]
}

MaterialHLSLEmitter.cpp

마지막 단계네요. 위에 있는 FHLSLMaterialTranslator::GetMaterialEnvironment와 같이 머티리얼을 설정하는데 필요한 부분인데 내용은 비슷해요

static void GetMaterialEnvironment(EShaderPlatform InPlatform,
	const FMaterial& InMaterial,
	const UE::HLSLTree::FEmitContext& EmitContext,
	const FMaterialCompilationOutput& MaterialCompilationOutput,
	FShaderCompilerEnvironment& OutEnvironment)
{
	// [...]
	
	// [ CUSTOM ] Adding custom shading model
	if (ShadingModels.HasShadingModel(MSM_CustomLit))
	{
		OutEnvironment.SetDefine(TEXT("MATERIAL_SHADINGMODEL_CUSTOM_LIT"), TEXT("1"));
		NumSetMaterials++;
	}
	
	// [...]
}

빌드하기

수정이 끝났으니 빌드를 해야겠죠. 빌드는 엔진 빌드를 먼저 해주고 프로젝트 빌드를 해주시면 되겠습니다.참고

빌드를 마치고 나면 머티리얼 에디터에 커스텀 셰이딩 모델이 추가된 것을 볼 수 있어요


마치며

셰이딩 모델을 추가하는데 있어서 꼭 필요한 소스 코드를 수정해 봤습니다. 본문에서 다룬 건 기본적인 환경에서의 세팅이에요. 프로젝트에서 사용할 환경이 다르다면 수정해야 할 코드가 더 필요할 수도 있는데 차후에 다뤄볼게요. 이어지는 글에서는 커스텀 셰이딩 모델을 사용할 수 있도록 셰이더 코드를 수정해 보겠습니다


참고

https://docs.unrealengine.com/5.3/en-US/downloading-unreal-engine-source-code/

 

Downloading Unreal Engine Source Code

Steps for connecting to the source code repository and downloading the latest build of Unreal Engine.

docs.unrealengine.com

https://medium.com/@solaslin/learning-unreal-engine-4-implement-cel-shading-w-outline-using-custom-shading-model-in-ue4-22-1-775bccdb9ffb

 

Learning Unreal Engine 4: Implement Cel-Shading w/ Outline Using Custom Shading Model in UE4.22 (1)

Create Your Own Shading Model

medium.com

https://dev.epicgames.com/community/learning/tutorials/k8Ve/unreal-engine-how-to-build-the-unreal-editor-github

 

How To build the Unreal Editor (GitHub) | Community tutorial

A step by step guide to building Unreal Engine from source code using GitHub.

dev.epicgames.com